VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/xfsvfs.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.0 KB
Line 
1/* $Id: xfsvfs.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - XFS Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2018-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FS
42#include <iprt/fsvfs.h>
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/avl.h>
47#include <iprt/file.h>
48#include <iprt/err.h>
49#include <iprt/list.h>
50#include <iprt/log.h>
51#include <iprt/mem.h>
52#include <iprt/string.h>
53#include <iprt/vfs.h>
54#include <iprt/vfslowlevel.h>
55#include <iprt/formats/xfs.h>
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/** The maximum allocation group cache size (in bytes). */
62#if ARCH_BITS >= 64
63# define RTFSXFS_MAX_AG_CACHE_SIZE _512K
64#else
65# define RTFSXFS_MAX_AG_CACHE_SIZE _128K
66#endif
67/** The maximum inode cache size (in bytes). */
68#if ARCH_BITS >= 64
69# define RTFSXFS_MAX_INODE_CACHE_SIZE _512K
70#else
71# define RTFSXFS_MAX_INODE_CACHE_SIZE _128K
72#endif
73/** The maximum extent tree cache size (in bytes). */
74#if ARCH_BITS >= 64
75# define RTFSXFS_MAX_BLOCK_CACHE_SIZE _512K
76#else
77# define RTFSXFS_MAX_BLOCK_CACHE_SIZE _128K
78#endif
79
80
81/*********************************************************************************************************************************
82* Structures and Typedefs *
83*********************************************************************************************************************************/
84/** Pointer to the XFS filesystem data. */
85typedef struct RTFSXFSVOL *PRTFSXFSVOL;
86
87
88/**
89 * Cached allocation group descriptor data.
90 */
91typedef struct RTFSXFSAG
92{
93 /** AVL tree node, indexed by the allocation group number. */
94 AVLU32NODECORE Core;
95 /** List node for the LRU list used for eviction. */
96 RTLISTNODE NdLru;
97 /** Reference counter. */
98 volatile uint32_t cRefs;
99 /** @todo */
100} RTFSXFSAG;
101/** Pointer to allocation group descriptor data. */
102typedef RTFSXFSAG *PRTFSXFSAG;
103
104
105/**
106 * In-memory inode.
107 */
108typedef struct RTFSXFSINODE
109{
110 /** AVL tree node, indexed by the inode number. */
111 AVLU64NODECORE Core;
112 /** List node for the inode LRU list used for eviction. */
113 RTLISTNODE NdLru;
114 /** Reference counter. */
115 volatile uint32_t cRefs;
116 /** Byte offset in the backing file where the inode is stored.. */
117 uint64_t offInode;
118 /** Inode data. */
119 RTFSOBJINFO ObjInfo;
120 /** Inode data fork format. */
121 uint8_t enmFormat;
122 /** Inode flags. */
123 uint16_t fFlags;
124 /** Inode version. */
125 uint8_t uVersion;
126 /** Number of extents in the data fork for XFS_INODE_FORMAT_EXTENTS. */
127 uint32_t cExtentsData;
128 /** Raw inode data. */
129 uint8_t abData[1];
130} RTFSXFSINODE;
131/** Pointer to an in-memory inode. */
132typedef RTFSXFSINODE *PRTFSXFSINODE;
133
134
135/**
136 * Block cache entry.
137 */
138typedef struct RTFSXFSBLOCKENTRY
139{
140 /** AVL tree node, indexed by the filesystem block number. */
141 AVLU64NODECORE Core;
142 /** List node for the inode LRU list used for eviction. */
143 RTLISTNODE NdLru;
144 /** Reference counter. */
145 volatile uint32_t cRefs;
146 /** The block data. */
147 uint8_t abData[1];
148} RTFSXFSBLOCKENTRY;
149/** Pointer to a block cache entry. */
150typedef RTFSXFSBLOCKENTRY *PRTFSXFSBLOCKENTRY;
151
152
153/**
154 * Open directory instance.
155 */
156typedef struct RTFSXFSDIR
157{
158 /** Volume this directory belongs to. */
159 PRTFSXFSVOL pVol;
160 /** The underlying inode structure. */
161 PRTFSXFSINODE pInode;
162 /** Set if we've reached the end of the directory enumeration. */
163 bool fNoMoreFiles;
164 /** Current offset into the directory where the next entry should be read. */
165 uint64_t offEntry;
166 /** Next entry index (for logging purposes). */
167 uint32_t idxEntry;
168} RTFSXFSDIR;
169/** Pointer to an open directory instance. */
170typedef RTFSXFSDIR *PRTFSXFSDIR;
171
172
173/**
174 * Open file instance.
175 */
176typedef struct RTFSXFSFILE
177{
178 /** Volume this directory belongs to. */
179 PRTFSXFSVOL pVol;
180 /** The underlying inode structure. */
181 PRTFSXFSINODE pInode;
182 /** Current offset into the file for I/O. */
183 RTFOFF offFile;
184} RTFSXFSFILE;
185/** Pointer to an open file instance. */
186typedef RTFSXFSFILE *PRTFSXFSFILE;
187
188
189/**
190 * XFS filesystem volume.
191 */
192typedef struct RTFSXFSVOL
193{
194 /** Handle to itself. */
195 RTVFS hVfsSelf;
196 /** The file, partition, or whatever backing the ext volume. */
197 RTVFSFILE hVfsBacking;
198 /** The size of the backing thingy. */
199 uint64_t cbBacking;
200
201 /** RTVFSMNT_F_XXX. */
202 uint32_t fMntFlags;
203 /** RTFSXFSVFS_F_XXX (currently none defined). */
204 uint32_t fXfsFlags;
205
206 /** Size of one sector. */
207 size_t cbSector;
208 /** Size of one block. */
209 size_t cbBlock;
210 /** Number of bits to shift for converting a block number to byte offset. */
211 uint32_t cBlockShift;
212 /** Number of blocks per allocation group. */
213 XFSAGNUMBER cBlocksPerAg;
214 /** Number of blocks per allocation group as log2. */
215 uint32_t cAgBlocksLog;
216 /** Number of allocation groups for this volume. */
217 uint32_t cAgs;
218 /** inode of the root directory. */
219 XFSINO uInodeRoot;
220 /** Inode size in bytes. */
221 size_t cbInode;
222 /** Number of inodes per block. */
223 uint32_t cInodesPerBlock;
224 /** Number of inodes per block as log2. */
225 uint32_t cInodesPerBlockLog;
226
227 /** @name Allocation group cache.
228 * @{ */
229 /** LRU list anchor. */
230 RTLISTANCHOR LstAgLru;
231 /** Root of the cached allocation group tree. */
232 AVLU32TREE AgRoot;
233 /** Size of the cached allocation groups. */
234 size_t cbAgs;
235 /** @} */
236
237 /** @name Inode cache.
238 * @{ */
239 /** LRU list anchor for the inode cache. */
240 RTLISTANCHOR LstInodeLru;
241 /** Root of the cached inode tree. */
242 AVLU64TREE InodeRoot;
243 /** Size of the cached inodes. */
244 size_t cbInodes;
245 /** @} */
246
247 /** @name Block cache.
248 * @{ */
249 /** LRU list anchor for the block cache. */
250 RTLISTANCHOR LstBlockLru;
251 /** Root of the cached block tree. */
252 AVLU64TREE BlockRoot;
253 /** Size of cached blocks. */
254 size_t cbBlocks;
255 /** @} */
256} RTFSXFSVOL;
257
258
259
260/*********************************************************************************************************************************
261* Internal Functions *
262*********************************************************************************************************************************/
263static int rtFsXfsVol_OpenDirByInode(PRTFSXFSVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir);
264
265#ifdef LOG_ENABLED
266/**
267 * Logs the XFS filesystem superblock.
268 *
269 * @returns nothing.
270 * @param iAg The allocation group number for the given super block.
271 * @param pSb Pointer to the superblock.
272 */
273static void rtFsXfsSb_Log(uint32_t iAg, PCXFSSUPERBLOCK pSb)
274{
275 if (LogIs2Enabled())
276 {
277 Log2(("XFS: Superblock %#RX32:\n", iAg));
278 Log2(("XFS: u32Magic %#RX32\n", RT_BE2H_U32(pSb->u32Magic)));
279 Log2(("XFS: cbBlock %RU32\n", RT_BE2H_U32(pSb->cbBlock)));
280 Log2(("XFS: cBlocks %RU64\n", RT_BE2H_U64(pSb->cBlocks)));
281 Log2(("XFS: cBlocksRtDev %RU64\n", RT_BE2H_U64(pSb->cBlocksRtDev)));
282 Log2(("XFS: cExtentsRtDev %RU64\n", RT_BE2H_U64(pSb->cExtentsRtDev)));
283 Log2(("XFS: abUuid <todo>\n"));
284 Log2(("XFS: uBlockJournal %#RX64\n", RT_BE2H_U64(pSb->uBlockJournal)));
285 Log2(("XFS: uInodeRoot %#RX64\n", RT_BE2H_U64(pSb->uInodeRoot)));
286 Log2(("XFS: uInodeBitmapRtExt %#RX64\n", RT_BE2H_U64(pSb->uInodeBitmapRtExt)));
287 Log2(("XFS: uInodeBitmapSummary %#RX64\n", RT_BE2H_U64(pSb->uInodeBitmapSummary)));
288 Log2(("XFS: cRtExtent %RU32\n", RT_BE2H_U32(pSb->cRtExtent)));
289 Log2(("XFS: cAgBlocks %RU32\n", RT_BE2H_U32(pSb->cAgBlocks)));
290 Log2(("XFS: cAg %RU32\n", RT_BE2H_U32(pSb->cAg)));
291 Log2(("XFS: cRtBitmapBlocks %RU32\n", RT_BE2H_U32(pSb->cRtBitmapBlocks)));
292 Log2(("XFS: cJournalBlocks %RU32\n", RT_BE2H_U32(pSb->cJournalBlocks)));
293 Log2(("XFS: fVersion %#RX16%s%s%s%s%s%s%s%s%s%s%s\n", RT_BE2H_U16(pSb->fVersion),
294 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_ATTR ? " attr" : "",
295 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_NLINK ? " nlink" : "",
296 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_QUOTA ? " quota" : "",
297 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_ALIGN ? " align" : "",
298 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_DALIGN ? " dalign" : "",
299 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_SHARED ? " shared" : "",
300 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_LOGV2 ? " logv2" : "",
301 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_SECTOR ? " sector" : "",
302 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_EXTFLG ? " extflg" : "",
303 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_DIRV2 ? " dirv2" : "",
304 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_FEAT2 ? " feat2" : ""));
305 Log2(("XFS: cbSector %RU16\n", RT_BE2H_U16(pSb->cbSector)));
306 Log2(("XFS: cbInode %RU16\n", RT_BE2H_U16(pSb->cbInode)));
307 Log2(("XFS: cIndoesPerBlock %RU16\n", RT_BE2H_U16(pSb->cInodesPerBlock)));
308 Log2(("XFS: achFsName %12s\n", &pSb->achFsName[0]));
309 Log2(("XFS: cBlockSzLog %RU8\n", pSb->cBlockSzLog));
310 Log2(("XFS: cSectorSzLog %RU8\n", pSb->cSectorSzLog));
311 Log2(("XFS: cInodeSzLog %RU8\n", pSb->cInodeSzLog));
312 Log2(("XFS: cInodesPerBlockLog %RU8\n", pSb->cInodesPerBlockLog));
313 Log2(("XFS: cAgBlocksLog %RU8\n", pSb->cAgBlocksLog));
314 Log2(("XFS: cExtentsRtDevLog %RU8\n", pSb->cExtentsRtDevLog));
315 Log2(("XFS: fInProgress %RU8\n", pSb->fInProgress));
316 Log2(("XFS: cInodeMaxPct %RU8\n", pSb->cInodeMaxPct));
317 Log2(("XFS: cInodesGlobal %#RX64\n", RT_BE2H_U64(pSb->cInodesGlobal)));
318 Log2(("XFS: cInodesGlobalFree %#RX64\n", RT_BE2H_U64(pSb->cInodesGlobalFree)));
319 Log2(("XFS: cBlocksFree %#RX64\n", RT_BE2H_U64(pSb->cBlocksFree)));
320 Log2(("XFS: cExtentsRtFree %#RX64\n", RT_BE2H_U64(pSb->cExtentsRtFree)));
321 Log2(("XFS: uInodeQuotaUsr %#RX64\n", RT_BE2H_U64(pSb->uInodeQuotaUsr)));
322 Log2(("XFS: uInodeQuotaGrp %#RX64\n", RT_BE2H_U64(pSb->uInodeQuotaGrp)));
323 Log2(("XFS: fQuotaFlags %#RX16\n", RT_BE2H_U16(pSb->fQuotaFlags)));
324 Log2(("XFS: fFlagsMisc %#RX8\n", pSb->fFlagsMisc));
325 Log2(("XFS: uSharedVn %#RX8\n", pSb->uSharedVn));
326 Log2(("XFS: cBlocksInodeAlignment %#RX32\n", RT_BE2H_U32(pSb->cBlocksInodeAlignment)));
327 Log2(("XFS: cBlocksRaidStripe %#RX32\n", RT_BE2H_U32(pSb->cBlocksRaidStripe)));
328 Log2(("XFS: cBlocksRaidWidth %#RX32\n", RT_BE2H_U32(pSb->cBlocksRaidWidth)));
329 Log2(("XFS: cDirBlockAllocLog %RU8\n", pSb->cDirBlockAllocLog));
330 Log2(("XFS: cLogDevSubVolSectorSzLog %RU8\n", pSb->cLogDevSubVolSectorSzLog));
331 Log2(("XFS: cLogDevSectorSzLog %RU16\n", RT_BE2H_U16(pSb->cLogDevSectorSzLog)));
332 Log2(("XFS: cLogDevRaidStripe %RU32\n", RT_BE2H_U32(pSb->cLogDevRaidStripe)));
333 Log2(("XFS: fFeatures2 %#RX32\n", RT_BE2H_U32(pSb->fFeatures2)));
334 Log2(("XFS: fFeaturesRw %#RX32\n", RT_BE2H_U32(pSb->fFeaturesRw)));
335 Log2(("XFS: fFeaturesRo %#RX32\n", RT_BE2H_U32(pSb->fFeaturesRo)));
336 Log2(("XFS: fFeaturesIncompatRw %#RX32\n", RT_BE2H_U32(pSb->fFeaturesIncompatRw)));
337 Log2(("XFS: fFeaturesJrnlIncompatRw %#RX32\n", RT_BE2H_U32(pSb->fFeaturesJrnlIncompatRw)));
338 Log2(("XFS: u32Chksum %#RX32\n", RT_BE2H_U32(pSb->u32Chksum)));
339 Log2(("XFS: u32SparseInodeAlignment %#RX32\n", RT_BE2H_U32(pSb->u32SparseInodeAlignment)));
340 Log2(("XFS: uInodeProjectQuota %#RX64\n", RT_BE2H_U64(pSb->uInodeProjectQuota)));
341 Log2(("XFS: uJrnlSeqSbUpdate %#RX64\n", RT_BE2H_U64(pSb->uJrnlSeqSbUpdate)));
342 Log2(("XFS: abUuidMeta <todo>\n"));
343 Log2(("XFS: uInodeRm %#RX64\n", RT_BE2H_U64(pSb->uInodeRm)));
344 }
345}
346
347#if 0 /* unused */
348/**
349 * Logs a AG free space block.
350 *
351 * @returns nothing.
352 * @param iAg The allocation group number for the given free space block.
353 * @param pAgf The AG free space block.
354 */
355static void rtFsXfsAgf_Log(uint32_t iAg, PCXFSAGF pAgf)
356{
357 if (LogIs2Enabled())
358 {
359 Log2(("XFS: AGF %#RX32:\n", iAg));
360 Log2(("XFS: u32Magic %#RX32\n", RT_BE2H_U32(pAgf->u32Magic)));
361 Log2(("XFS: uVersion %#RX32\n", RT_BE2H_U32(pAgf->uVersion)));
362 Log2(("XFS: uSeqNo %#RX32\n", RT_BE2H_U32(pAgf->uSeqNo)));
363 Log2(("XFS: cLengthBlocks %#RX32\n", RT_BE2H_U32(pAgf->cLengthBlocks)));
364 Log2(("XFS: auRoots[0] %#RX32\n", RT_BE2H_U32(pAgf->auRoots[0])));
365 Log2(("XFS: auRoots[1] %#RX32\n", RT_BE2H_U32(pAgf->auRoots[1])));
366 Log2(("XFS: auRoots[2] %#RX32\n", RT_BE2H_U32(pAgf->auRoots[2])));
367 Log2(("XFS: acLvls[0] %RU32\n", RT_BE2H_U32(pAgf->acLvls[0])));
368 Log2(("XFS: acLvls[1] %RU32\n", RT_BE2H_U32(pAgf->acLvls[1])));
369 Log2(("XFS: acLvls[2] %RU32\n", RT_BE2H_U32(pAgf->acLvls[2])));
370 Log2(("XFS: idxFreeListFirst %RU32\n", RT_BE2H_U32(pAgf->idxFreeListFirst)));
371 Log2(("XFS: idxFreeListLast %RU32\n", RT_BE2H_U32(pAgf->idxFreeListLast)));
372 Log2(("XFS: cFreeListBlocks %RU32\n", RT_BE2H_U32(pAgf->cFreeListBlocks)));
373 Log2(("XFS: cFreeBlocks %RU32\n", RT_BE2H_U32(pAgf->cFreeBlocks)));
374 Log2(("XFS: cFreeBlocksLongest %RU32\n", RT_BE2H_U32(pAgf->cFreeBlocksLongest)));
375 Log2(("XFS: cBlocksBTrees %RU32\n", RT_BE2H_U32(pAgf->cBlocksBTrees)));
376 Log2(("XFS: abUuid <todo>\n"));
377 Log2(("XFS: cBlocksRevMap %RU32\n", RT_BE2H_U32(pAgf->cBlocksRevMap)));
378 Log2(("XFS: cBlocksRefcountBTree %RU32\n", RT_BE2H_U32(pAgf->cBlocksRefcountBTree)));
379 Log2(("XFS: uRootRefcount %#RX32\n", RT_BE2H_U32(pAgf->uRootRefcount)));
380 Log2(("XFS: cLvlRefcount %RU32\n", RT_BE2H_U32(pAgf->cLvlRefcount)));
381 Log2(("XFS: uSeqNoLastWrite %#RX64\n", RT_BE2H_U64(pAgf->uSeqNoLastWrite)));
382 Log2(("XFS: uChkSum %#RX32\n", RT_BE2H_U32(pAgf->uChkSum)));
383 }
384}
385#endif
386
387/**
388 * Loads an AG inode information block.
389 *
390 * @returns nothing.
391 * @param iAg The allocation group number for the given inode information block.
392 * @param pAgi The AG inode information block.
393 */
394static void rtFsXfsAgi_Log(uint32_t iAg, PCXFSAGI pAgi)
395{
396 if (LogIs2Enabled())
397 {
398 Log2(("XFS: AGI %#RX32:\n", iAg));
399 Log2(("XFS: u32Magic %#RX32\n", RT_BE2H_U32(pAgi->u32Magic)));
400 Log2(("XFS: uVersion %#RX32\n", RT_BE2H_U32(pAgi->uVersion)));
401 Log2(("XFS: uSeqNo %#RX32\n", RT_BE2H_U32(pAgi->uSeqNo)));
402 Log2(("XFS: cLengthBlocks %#RX32\n", RT_BE2H_U32(pAgi->cLengthBlocks)));
403 Log2(("XFS: cInodesAlloc %#RX32\n", RT_BE2H_U32(pAgi->cInodesAlloc)));
404 Log2(("XFS: uRootInode %#RX32\n", RT_BE2H_U32(pAgi->uRootInode)));
405 Log2(("XFS: cLvlsInode %RU32\n", RT_BE2H_U32(pAgi->cLvlsInode)));
406 Log2(("XFS: uInodeNew %#RX32\n", RT_BE2H_U32(pAgi->uInodeNew)));
407 Log2(("XFS: uInodeDir %#RX32\n", RT_BE2H_U32(pAgi->uInodeDir)));
408 Log2(("XFS: au32HashUnlinked[0..63] <todo>\n"));
409 Log2(("XFS: abUuid <todo>\n"));
410 Log2(("XFS: uChkSum %#RX32\n", RT_BE2H_U32(pAgi->uChkSum)));
411 Log2(("XFS: uSeqNoLastWrite %#RX64\n", RT_BE2H_U64(pAgi->uSeqNoLastWrite)));
412 Log2(("XFS: uRootFreeInode %#RX32\n", RT_BE2H_U32(pAgi->uRootFreeInode)));
413 Log2(("XFS: cLvlsFreeInode %RU32\n", RT_BE2H_U32(pAgi->cLvlsFreeInode)));
414 }
415}
416
417
418/**
419 * Logs a XFS filesystem inode.
420 *
421 * @returns nothing.
422 * @param pThis The XFS volume instance.
423 * @param iInode Inode number.
424 * @param pInode Pointer to the inode.
425 */
426static void rtFsXfsInode_Log(PRTFSXFSVOL pThis, XFSINO iInode, PCXFSINODECORE pInode)
427{
428 RT_NOREF(pThis);
429
430 if (LogIs2Enabled())
431 {
432 RTTIMESPEC Spec;
433 char sz[80];
434
435 Log2(("XFS: Inode %#RX64:\n", iInode));
436 Log2(("XFS: u16Magic %#RX16\n", RT_BE2H_U16(pInode->u16Magic)));
437 Log2(("XFS: fMode %#RX16\n", RT_BE2H_U16(pInode->fMode)));
438 Log2(("XFS: iVersion %#RX8\n", pInode->iVersion));
439 Log2(("XFS: enmFormat %#RX8\n", pInode->enmFormat));
440 Log2(("XFS: cOnLinks %RU16\n", RT_BE2H_U16(pInode->cOnLinks)));
441 Log2(("XFS: uUid %#RX32\n", RT_BE2H_U32(pInode->uUid)));
442 Log2(("XFS: uGid %#RX32\n", RT_BE2H_U32(pInode->uGid)));
443 Log2(("XFS: cLinks %#RX32\n", RT_BE2H_U32(pInode->cLinks)));
444 Log2(("XFS: uProjIdLow %#RX16\n", RT_BE2H_U16(pInode->uProjIdLow)));
445 Log2(("XFS: uProjIdHigh %#RX16\n", RT_BE2H_U16(pInode->uProjIdHigh)));
446 Log2(("XFS: cFlush %RU16\n", RT_BE2H_U16(pInode->cFlush)));
447 Log2(("XFS: TsLastAccessed %#RX32:%#RX32 %s\n", RT_BE2H_U32(pInode->TsLastAccessed.cSecEpoch),
448 RT_BE2H_U32(pInode->TsLastAccessed.cNanoSec),
449 RTTimeSpecToString(RTTimeSpecAddNano(RTTimeSpecSetSeconds(&Spec, RT_BE2H_U32(pInode->TsLastAccessed.cSecEpoch)),
450 RT_BE2H_U32(pInode->TsLastAccessed.cNanoSec)),
451 sz, sizeof(sz))));
452 Log2(("XFS: TsLastModified %#RX32:%#RX32 %s\n", RT_BE2H_U32(pInode->TsLastModified.cSecEpoch),
453 RT_BE2H_U32(pInode->TsLastModified.cNanoSec),
454 RTTimeSpecToString(RTTimeSpecAddNano(RTTimeSpecSetSeconds(&Spec, RT_BE2H_U32(pInode->TsLastModified.cSecEpoch)),
455 RT_BE2H_U32(pInode->TsLastModified.cNanoSec)),
456 sz, sizeof(sz))));
457 Log2(("XFS: TsCreatedModified %#RX32:%#RX32 %s\n", RT_BE2H_U32(pInode->TsCreatedModified.cSecEpoch),
458 RT_BE2H_U32(pInode->TsCreatedModified.cNanoSec),
459 RTTimeSpecToString(RTTimeSpecAddNano(RTTimeSpecSetSeconds(&Spec, RT_BE2H_U32(pInode->TsCreatedModified.cSecEpoch)),
460 RT_BE2H_U32(pInode->TsCreatedModified.cNanoSec)),
461 sz, sizeof(sz))));
462 Log2(("XFS: cbInode %#RX64\n", RT_BE2H_U64(pInode->cbInode)));
463 Log2(("XFS: cBlocks %#RX64\n", RT_BE2H_U64(pInode->cBlocks)));
464 Log2(("XFS: cExtentBlocksMin %#RX32\n", RT_BE2H_U32(pInode->cExtentBlocksMin)));
465 Log2(("XFS: cExtentsData %#RX32\n", RT_BE2H_U32(pInode->cExtentsData)));
466 Log2(("XFS: cExtentsAttr %#RX16\n", RT_BE2H_U16(pInode->cExtentsAttr)));
467 Log2(("XFS: offAttrFork %#RX8\n", pInode->offAttrFork));
468 Log2(("XFS: enmFormatAttr %#RX8\n", pInode->enmFormatAttr));
469 Log2(("XFS: fEvtMaskDmig %#RX32\n", RT_BE2H_U32(pInode->fEvtMaskDmig)));
470 Log2(("XFS: uStateDmig %#RX16\n", RT_BE2H_U16(pInode->uStateDmig)));
471 Log2(("XFS: fFlags %#RX16\n", RT_BE2H_U16(pInode->fFlags)));
472 Log2(("XFS: cGeneration %#RX32\n", RT_BE2H_U32(pInode->cGeneration)));
473 Log2(("XFS: offBlockUnlinkedNext %#RX32\n", RT_BE2H_U32(pInode->offBlockUnlinkedNext)));
474 Log2(("XFS: uChkSum %#RX32\n", RT_BE2H_U32(pInode->uChkSum)));
475 Log2(("XFS: cAttrChanges %#RX64\n", RT_BE2H_U64(pInode->cAttrChanges)));
476 Log2(("XFS: uFlushSeqNo %#RX64\n", RT_BE2H_U64(pInode->uFlushSeqNo)));
477 Log2(("XFS: fFlags2 %#RX64\n", RT_BE2H_U64(pInode->fFlags2)));
478 Log2(("XFS: cExtentCowMin %#RX32\n", RT_BE2H_U32(pInode->cExtentCowMin)));
479 Log2(("XFS: TsCreation %#RX32:%#RX32 %s\n", RT_BE2H_U32(pInode->TsCreation.cSecEpoch),
480 RT_BE2H_U32(pInode->TsCreation.cNanoSec),
481 RTTimeSpecToString(RTTimeSpecAddNano(RTTimeSpecSetSeconds(&Spec, RT_BE2H_U32(pInode->TsCreation.cSecEpoch)),
482 RT_BE2H_U32(pInode->TsCreation.cNanoSec)),
483 sz, sizeof(sz))));
484 Log2(("XFS: uInode %#RX64\n", RT_BE2H_U64(pInode->uInode)));
485 Log2(("XFS: abUuid <todo>\n"));
486 }
487}
488
489
490#if 0
491/**
492 * Logs a XFS filesystem directory entry.
493 *
494 * @returns nothing.
495 * @param pThis The XFS volume instance.
496 * @param idxDirEntry Directory entry index number.
497 * @param pDirEntry The directory entry.
498 */
499static void rtFsXfsDirEntry_Log(PRTFSXFSVOL pThis, uint32_t idxDirEntry, PCXFSDIRENTRYEX pDirEntry)
500{
501 if (LogIs2Enabled())
502 {
503 }
504}
505#endif
506#endif
507
508
509/**
510 * Converts a block number to a byte offset.
511 *
512 * @returns Offset in bytes for the given block number.
513 * @param pThis The XFS volume instance.
514 * @param iBlock The block number to convert.
515 */
516DECLINLINE(uint64_t) rtFsXfsBlockIdxToDiskOffset(PRTFSXFSVOL pThis, uint64_t iBlock)
517{
518 return iBlock << pThis->cBlockShift;
519}
520
521
522/**
523 * Converts a byte offset to a block number.
524 *
525 * @returns Block number.
526 * @param pThis The XFS volume instance.
527 * @param iBlock The offset to convert.
528 */
529DECLINLINE(uint64_t) rtFsXfsDiskOffsetToBlockIdx(PRTFSXFSVOL pThis, uint64_t off)
530{
531 return off >> pThis->cBlockShift;
532}
533
534
535/**
536 * Splits the given absolute inode number into the AG number, block inside the AG
537 * and the offset into the block where to find the inode structure.
538 *
539 * @returns nothing.
540 * @param pThis The XFS volume instance.
541 * @param iInode The inode to split.
542 * @param piAg Where to store the AG number.
543 * @param puBlock Where to store the block number inside the AG.
544 * @param poffBlock Where to store the offset into the block.
545 */
546DECLINLINE(void) rtFsXfsInodeSplitAbs(PRTFSXFSVOL pThis, XFSINO iInode,
547 uint32_t *piAg, uint32_t *puBlock,
548 uint32_t *poffBlock)
549{
550 *poffBlock = iInode & (pThis->cInodesPerBlock - 1);
551 iInode >>= pThis->cInodesPerBlockLog;
552 *puBlock = iInode & (RT_BIT_32(pThis->cAgBlocksLog) - 1); /* Using the log2 value here as it is rounded. */
553 iInode >>= RT_BIT_32(pThis->cAgBlocksLog) - 1;
554 *piAg = (uint32_t)iInode;
555}
556
557
558/**
559 * Returns the size of the core inode structure on disk for the given version.
560 *
561 * @returns Size of the on disk inode structure in bytes.
562 * @param uVersion The inode version.
563 */
564DECLINLINE(size_t) rtFsXfsInodeGetSz(uint8_t uVersion)
565{
566 if (uVersion < 3)
567 return RT_OFFSETOF(XFSINODECORE, uChkSum);
568 return sizeof(XFSINODECORE);
569}
570
571
572/**
573 * Returns the pointer to the data fork of the given inode.
574 *
575 * @returns Pointer to the data fork.
576 * @param pThis The XFS volume instance.
577 * @param pInode The inode to get the data fork for.
578 * @param pcb Where to store the size of the remaining data area beginning with the fork.
579 */
580DECLINLINE(void *) rtFsXfsInodeGetDataFork(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode, size_t *pcb)
581{
582 size_t offDataFork = rtFsXfsInodeGetSz(pInode->uVersion);
583 size_t cbInodeData = pThis->cbInode - offDataFork;
584 if (pcb)
585 *pcb = cbInodeData;
586
587 return &pInode->abData[offDataFork];
588}
589
590
591/**
592 * Allocates a new block group.
593 *
594 * @returns Pointer to the new block group descriptor or NULL if out of memory.
595 * @param pThis The XFS volume instance.
596 * @param cbAlloc How much to allocate.
597 * @param iBlockGroup Block group number.
598 */
599static PRTFSXFSBLOCKENTRY rtFsXfsVol_BlockAlloc(PRTFSXFSVOL pThis, size_t cbAlloc, uint64_t iBlock)
600{
601 PRTFSXFSBLOCKENTRY pBlock = (PRTFSXFSBLOCKENTRY)RTMemAllocZ(cbAlloc);
602 if (RT_LIKELY(pBlock))
603 {
604 pBlock->Core.Key = iBlock;
605 pBlock->cRefs = 0;
606 pThis->cbBlocks += cbAlloc;
607 }
608
609 return pBlock;
610}
611
612
613/**
614 * Returns a new block entry utilizing the cache if possible.
615 *
616 * @returns Pointer to the new block entry or NULL if out of memory.
617 * @param pThis The XFS volume instance.
618 * @param iBlock Block number.
619 */
620static PRTFSXFSBLOCKENTRY rtFsXfsVol_BlockGetNew(PRTFSXFSVOL pThis, uint64_t iBlock)
621{
622 PRTFSXFSBLOCKENTRY pBlock = NULL;
623 size_t cbAlloc = RT_UOFFSETOF_DYN(RTFSXFSBLOCKENTRY, abData[pThis->cbBlock]);
624 if (pThis->cbBlocks + cbAlloc <= RTFSXFS_MAX_BLOCK_CACHE_SIZE)
625 pBlock = rtFsXfsVol_BlockAlloc(pThis, cbAlloc, iBlock);
626 else
627 {
628 pBlock = RTListRemoveLast(&pThis->LstBlockLru, RTFSXFSBLOCKENTRY, NdLru);
629 if (!pBlock)
630 pBlock = rtFsXfsVol_BlockAlloc(pThis, cbAlloc, iBlock);
631 else
632 {
633 /* Remove the block group from the tree because it gets a new key. */
634 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
635 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
636 }
637 }
638
639 Assert(!pBlock->cRefs);
640 pBlock->Core.Key = iBlock;
641 pBlock->cRefs = 1;
642
643 return pBlock;
644}
645
646
647/**
648 * Frees the given block.
649 *
650 * @returns nothing.
651 * @param pThis The XFS volume instance.
652 * @param pBlock The block to free.
653 */
654static void rtFsXfsVol_BlockFree(PRTFSXFSVOL pThis, PRTFSXFSBLOCKENTRY pBlock)
655{
656 Assert(!pBlock->cRefs);
657
658 /*
659 * Put it into the cache if the limit wasn't exceeded, otherwise the block group
660 * is freed right away.
661 */
662 if (pThis->cbBlocks <= RTFSXFS_MAX_BLOCK_CACHE_SIZE)
663 {
664 /* Put onto the LRU list. */
665 RTListPrepend(&pThis->LstBlockLru, &pBlock->NdLru);
666 }
667 else
668 {
669 /* Remove from the tree and free memory. */
670 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
671 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
672 RTMemFree(pBlock);
673 pThis->cbBlocks -= RT_UOFFSETOF_DYN(RTFSXFSBLOCKENTRY, abData[pThis->cbBlock]);
674 }
675}
676
677
678/**
679 * Gets the specified block data from the volume.
680 *
681 * @returns IPRT status code.
682 * @param pThis The XFS volume instance.
683 * @param iBlock The filesystem block to load.
684 * @param ppBlock Where to return the pointer to the block entry on success.
685 * @param ppvData Where to return the pointer to the block data on success.
686 */
687static int rtFsXfsVol_BlockLoad(PRTFSXFSVOL pThis, uint64_t iBlock, PRTFSXFSBLOCKENTRY *ppBlock, void **ppvData)
688{
689 int rc = VINF_SUCCESS;
690
691 /* Try to fetch the block group from the cache first. */
692 PRTFSXFSBLOCKENTRY pBlock = (PRTFSXFSBLOCKENTRY)RTAvlU64Get(&pThis->BlockRoot, iBlock);
693 if (!pBlock)
694 {
695 /* Slow path, load from disk. */
696 pBlock = rtFsXfsVol_BlockGetNew(pThis, iBlock);
697 if (RT_LIKELY(pBlock))
698 {
699 uint64_t offRead = rtFsXfsBlockIdxToDiskOffset(pThis, iBlock);
700 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlock->abData[0], pThis->cbBlock, NULL);
701 if (RT_SUCCESS(rc))
702 {
703 bool fIns = RTAvlU64Insert(&pThis->BlockRoot, &pBlock->Core);
704 Assert(fIns); RT_NOREF(fIns);
705 }
706 }
707 else
708 rc = VERR_NO_MEMORY;
709 }
710 else
711 {
712 /* Remove from current LRU list position and add to the beginning. */
713 uint32_t cRefs = ASMAtomicIncU32(&pBlock->cRefs);
714 if (cRefs == 1) /* Blocks get removed from the LRU list if they are referenced. */
715 RTListNodeRemove(&pBlock->NdLru);
716 }
717
718 if (RT_SUCCESS(rc))
719 {
720 *ppBlock = pBlock;
721 *ppvData = &pBlock->abData[0];
722 }
723 else if (pBlock)
724 {
725 ASMAtomicDecU32(&pBlock->cRefs);
726 rtFsXfsVol_BlockFree(pThis, pBlock); /* Free the block. */
727 }
728
729 return rc;
730}
731
732
733/**
734 * Releases a reference of the given block.
735 *
736 * @returns nothing.
737 * @param pThis The XFS volume instance.
738 * @param pBlock The block to release.
739 */
740static void rtFsXfsVol_BlockRelease(PRTFSXFSVOL pThis, PRTFSXFSBLOCKENTRY pBlock)
741{
742 uint32_t cRefs = ASMAtomicDecU32(&pBlock->cRefs);
743 if (!cRefs)
744 rtFsXfsVol_BlockFree(pThis, pBlock);
745}
746
747#if 0 /* unused */
748/**
749 * Allocates a new alloction group.
750 *
751 * @returns Pointer to the new allocation group descriptor or NULL if out of memory.
752 * @param pThis The XFS volume instance.
753 * @param iAG Allocation group number.
754 */
755static PRTFSXFSAG rtFsXfsAg_Alloc(PRTFSXFSVOL pThis, uint32_t iAg)
756{
757 PRTFSXFSAG pAg = (PRTFSXFSAG)RTMemAllocZ(sizeof(RTFSXFSAG));
758 if (RT_LIKELY(pAg))
759 {
760 pAg->Core.Key = iAg;
761 pAg->cRefs = 0;
762 pThis->cbAgs += sizeof(RTFSXFSAG);
763 }
764
765 return pAg;
766}
767
768
769/**
770 * Frees the given allocation group.
771 *
772 * @returns nothing.
773 * @param pThis The XFS volume instance.
774 * @param pAg The allocation group to free.
775 */
776static void rtFsXfsAg_Free(PRTFSXFSVOL pThis, PRTFSXFSAG pAg)
777{
778 Assert(!pAg->cRefs);
779
780 /*
781 * Put it into the cache if the limit wasn't exceeded, otherwise the allocation group
782 * is freed right away.
783 */
784 if (pThis->cbAgs <= RTFSXFS_MAX_AG_CACHE_SIZE)
785 {
786 /* Put onto the LRU list. */
787 RTListPrepend(&pThis->LstAgLru, &pAg->NdLru);
788 }
789 else
790 {
791 /* Remove from the tree and free memory. */
792 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->AgRoot, pAg->Core.Key);
793 Assert(pCore == &pAg->Core); RT_NOREF(pCore);
794 RTMemFree(pAg);
795 pThis->cbAgs -= sizeof(RTFSXFSAG);
796 }
797}
798
799
800/**
801 * Returns a new block group utilizing the cache if possible.
802 *
803 * @returns Pointer to the new block group descriptor or NULL if out of memory.
804 * @param pThis The XFS volume instance.
805 * @param iAg Allocation group number.
806 */
807static PRTFSXFSAG rtFsXfsAg_GetNew(PRTFSXFSVOL pThis, uint32_t iAg)
808{
809 PRTFSXFSAG pAg = NULL;
810 if (pThis->cbAgs + sizeof(RTFSXFSAG) <= RTFSXFS_MAX_AG_CACHE_SIZE)
811 pAg = rtFsXfsAg_Alloc(pThis, iAg);
812 else
813 {
814 pAg = RTListRemoveLast(&pThis->LstAgLru, RTFSXFSAG, NdLru);
815 if (!pAg)
816 pAg = rtFsXfsAg_Alloc(pThis, iAg);
817 else
818 {
819 /* Remove the block group from the tree because it gets a new key. */
820 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->AgRoot, pAg->Core.Key);
821 Assert(pCore == &pAg->Core); RT_NOREF(pCore);
822 }
823 }
824
825 Assert(!pAg->cRefs);
826 pAg->Core.Key = iAg;
827 pAg->cRefs = 1;
828
829 return pAg;
830}
831
832
833/**
834 * Loads the given allocation group number and returns it on success.
835 *
836 * @returns IPRT status code.
837 * @param pThis The XFS volume instance.
838 * @param iAg The allocation group to load.
839 * @param ppAg Where to store the allocation group on success.
840 */
841static int rtFsXfsAg_Load(PRTFSXFSVOL pThis, uint32_t iAg, PRTFSXFSAG *ppAg)
842{
843 int rc = VINF_SUCCESS;
844
845 AssertReturn(iAg < pThis->cAgs, VERR_VFS_BOGUS_FORMAT);
846
847 /* Try to fetch the allocation group from the cache first. */
848 PRTFSXFSAG pAg = (PRTFSXFSAG)RTAvlU32Get(&pThis->AgRoot, iAg);
849 if (!pAg)
850 {
851 /* Slow path, load from disk. */
852 pAg = rtFsXfsAg_GetNew(pThis, iAg);
853 if (RT_LIKELY(pAg))
854 {
855 uint64_t offRead = rtFsXfsBlockIdxToDiskOffset(pThis, iAg * pThis->cBlocksPerAg);
856 XFSSUPERBLOCK Sb;
857 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &Sb, sizeof(Sb), NULL);
858 if (RT_SUCCESS(rc))
859 {
860#ifdef LOG_ENABLED
861 rtFsXfsSb_Log(iAg, &Sb);
862#endif
863 }
864 }
865 else
866 rc = VERR_NO_MEMORY;
867 }
868 else
869 {
870 /* Remove from current LRU list position and add to the beginning. */
871 uint32_t cRefs = ASMAtomicIncU32(&pAg->cRefs);
872 if (cRefs == 1) /* Block groups get removed from the LRU list if they are referenced. */
873 RTListNodeRemove(&pAg->NdLru);
874 }
875
876 if (RT_SUCCESS(rc))
877 *ppAg = pAg;
878 else if (pAg)
879 {
880 ASMAtomicDecU32(&pAg->cRefs);
881 rtFsXfsAg_Free(pThis, pAg); /* Free the allocation group. */
882 }
883
884 return rc;
885}
886
887
888/**
889 * Releases a reference of the given allocation group.
890 *
891 * @returns nothing.
892 * @param pThis The XFS volume instance.
893 * @param pAg The allocation group to release.
894 */
895static void rtFsXfsAg_Release(PRTFSXFSVOL pThis, PRTFSXFSAG pAg)
896{
897 uint32_t cRefs = ASMAtomicDecU32(&pAg->cRefs);
898 if (!cRefs)
899 rtFsXfsAg_Free(pThis, pAg);
900}
901#endif
902
903/**
904 * Allocates a new inode.
905 *
906 * @returns Pointer to the new inode or NULL if out of memory.
907 * @param pThis The XFS volume instance.
908 * @param iInode Inode number.
909 */
910static PRTFSXFSINODE rtFsXfsInode_Alloc(PRTFSXFSVOL pThis, uint32_t iInode)
911{
912 size_t cbAlloc = RT_UOFFSETOF_DYN(RTFSXFSINODE, abData[pThis->cbInode]);
913 PRTFSXFSINODE pInode = (PRTFSXFSINODE)RTMemAllocZ(cbAlloc);
914 if (RT_LIKELY(pInode))
915 {
916 pInode->Core.Key = iInode;
917 pInode->cRefs = 0;
918 pThis->cbInodes += cbAlloc;
919 }
920
921 return pInode;
922}
923
924
925/**
926 * Frees the given inode.
927 *
928 * @returns nothing.
929 * @param pThis The XFS volume instance.
930 * @param pInode The inode to free.
931 */
932static void rtFsXfsInode_Free(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode)
933{
934 Assert(!pInode->cRefs);
935
936 /*
937 * Put it into the cache if the limit wasn't exceeded, otherwise the inode
938 * is freed right away.
939 */
940 if (pThis->cbInodes <= RTFSXFS_MAX_INODE_CACHE_SIZE)
941 {
942 /* Put onto the LRU list. */
943 RTListPrepend(&pThis->LstInodeLru, &pInode->NdLru);
944 }
945 else
946 {
947 /* Remove from the tree and free memory. */
948 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->InodeRoot, pInode->Core.Key);
949 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
950 RTMemFree(pInode);
951 pThis->cbInodes -= RT_UOFFSETOF_DYN(RTFSXFSINODE, abData[pThis->cbInode]);
952 }
953}
954
955
956/**
957 * Returns a new inodep utilizing the cache if possible.
958 *
959 * @returns Pointer to the new inode or NULL if out of memory.
960 * @param pThis The XFS volume instance.
961 * @param iInode Inode number.
962 */
963static PRTFSXFSINODE rtFsXfsInode_GetNew(PRTFSXFSVOL pThis, XFSINO iInode)
964{
965 PRTFSXFSINODE pInode = NULL;
966 if (pThis->cbInodes + RT_UOFFSETOF_DYN(RTFSXFSINODE, abData[pThis->cbInode]) <= RTFSXFS_MAX_INODE_CACHE_SIZE)
967 pInode = rtFsXfsInode_Alloc(pThis, iInode);
968 else
969 {
970 pInode = RTListRemoveLast(&pThis->LstInodeLru, RTFSXFSINODE, NdLru);
971 if (!pInode)
972 pInode = rtFsXfsInode_Alloc(pThis, iInode);
973 else
974 {
975 /* Remove the block group from the tree because it gets a new key. */
976 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->InodeRoot, pInode->Core.Key);
977 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
978 }
979 }
980
981 Assert(!pInode->cRefs);
982 pInode->Core.Key = iInode;
983 pInode->cRefs = 1;
984
985 return pInode;
986}
987
988
989/**
990 * Loads the given inode number and returns it on success.
991 *
992 * @returns IPRT status code.
993 * @param pThis The XFS volume instance.
994 * @param iInode The inode to load.
995 * @param ppInode Where to store the inode on success.
996 */
997static int rtFsXfsInode_Load(PRTFSXFSVOL pThis, XFSINO iInode, PRTFSXFSINODE *ppInode)
998{
999 int rc = VINF_SUCCESS;
1000
1001 /* Try to fetch the inode from the cache first. */
1002 PRTFSXFSINODE pInode = (PRTFSXFSINODE)RTAvlU64Get(&pThis->InodeRoot, iInode);
1003 if (!pInode)
1004 {
1005 /* Slow path, load from disk. */
1006 pInode = rtFsXfsInode_GetNew(pThis, iInode);
1007 if (RT_LIKELY(pInode))
1008 {
1009 uint32_t iAg;
1010 uint32_t uBlock;
1011 uint32_t offBlock;
1012
1013 rtFsXfsInodeSplitAbs(pThis, iInode, &iAg, &uBlock, &offBlock);
1014
1015 uint64_t offRead = (iAg * pThis->cBlocksPerAg + uBlock) * pThis->cbBlock + offBlock;
1016 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pInode->abData[0], pThis->cbInode, NULL);
1017 if (RT_SUCCESS(rc))
1018 {
1019 PCXFSINODECORE pInodeCore = (PCXFSINODECORE)&pInode->abData[0];
1020
1021#ifdef LOG_ENABLED
1022 rtFsXfsInode_Log(pThis, iInode, pInodeCore);
1023#endif
1024
1025 pInode->offInode = offRead;
1026 pInode->fFlags = RT_BE2H_U16(pInodeCore->fFlags);
1027 pInode->enmFormat = pInodeCore->enmFormat;
1028 pInode->cExtentsData = RT_BE2H_U32(pInodeCore->cExtentsData);
1029 pInode->ObjInfo.cbObject = RT_BE2H_U64(pInodeCore->cbInode);
1030 pInode->ObjInfo.cbAllocated = RT_BE2H_U64(pInodeCore->cBlocks) * pThis->cbBlock;
1031 RTTimeSpecSetSeconds(&pInode->ObjInfo.AccessTime, RT_BE2H_U32(pInodeCore->TsLastAccessed.cSecEpoch));
1032 RTTimeSpecAddNano(&pInode->ObjInfo.AccessTime, RT_BE2H_U32(pInodeCore->TsLastAccessed.cNanoSec));
1033 RTTimeSpecSetSeconds(&pInode->ObjInfo.ModificationTime, RT_BE2H_U32(pInodeCore->TsLastModified.cSecEpoch));
1034 RTTimeSpecAddNano(&pInode->ObjInfo.ModificationTime, RT_BE2H_U32(pInodeCore->TsLastModified.cNanoSec));
1035 RTTimeSpecSetSeconds(&pInode->ObjInfo.ChangeTime, RT_BE2H_U32(pInodeCore->TsCreatedModified.cSecEpoch));
1036 RTTimeSpecAddNano(&pInode->ObjInfo.ChangeTime, RT_BE2H_U32(pInodeCore->TsCreatedModified.cNanoSec));
1037 pInode->ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1038 pInode->ObjInfo.Attr.u.Unix.uid = RT_BE2H_U32(pInodeCore->uUid);
1039 pInode->ObjInfo.Attr.u.Unix.gid = RT_BE2H_U32(pInodeCore->uGid);
1040 pInode->ObjInfo.Attr.u.Unix.cHardlinks = RT_BE2H_U16(pInodeCore->cOnLinks); /** @todo v2 inodes. */
1041 pInode->ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1042 pInode->ObjInfo.Attr.u.Unix.INodeId = iInode;
1043 pInode->ObjInfo.Attr.u.Unix.fFlags = 0;
1044 pInode->ObjInfo.Attr.u.Unix.GenerationId = RT_BE2H_U32(pInodeCore->cGeneration);
1045 pInode->ObjInfo.Attr.u.Unix.Device = 0;
1046 if (pInodeCore->iVersion >= 3)
1047 {
1048 RTTimeSpecSetSeconds(&pInode->ObjInfo.BirthTime, RT_BE2H_U32(pInodeCore->TsCreation.cSecEpoch));
1049 RTTimeSpecAddNano(&pInode->ObjInfo.BirthTime, RT_BE2H_U32(pInodeCore->TsCreation.cNanoSec));
1050 }
1051 else
1052 pInode->ObjInfo.BirthTime = pInode->ObjInfo.ChangeTime;
1053
1054 /* Fill in the mode. */
1055 pInode->ObjInfo.Attr.fMode = 0;
1056 uint16_t fInodeMode = RT_BE2H_U16(pInodeCore->fMode);
1057 switch (XFS_INODE_MODE_TYPE_GET_TYPE(fInodeMode))
1058 {
1059 case XFS_INODE_MODE_TYPE_FIFO:
1060 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FIFO;
1061 break;
1062 case XFS_INODE_MODE_TYPE_CHAR:
1063 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_CHAR;
1064 break;
1065 case XFS_INODE_MODE_TYPE_DIR:
1066 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DIRECTORY;
1067 break;
1068 case XFS_INODE_MODE_TYPE_BLOCK:
1069 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_BLOCK;
1070 break;
1071 case XFS_INODE_MODE_TYPE_REGULAR:
1072 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FILE;
1073 break;
1074 case XFS_INODE_MODE_TYPE_SYMLINK:
1075 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SYMLINK;
1076 break;
1077 case XFS_INODE_MODE_TYPE_SOCKET:
1078 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SOCKET;
1079 break;
1080 default:
1081 rc = VERR_VFS_BOGUS_FORMAT;
1082 }
1083 if (fInodeMode & XFS_INODE_MODE_EXEC_OTHER)
1084 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXOTH;
1085 if (fInodeMode & XFS_INODE_MODE_WRITE_OTHER)
1086 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWOTH;
1087 if (fInodeMode & XFS_INODE_MODE_READ_OTHER)
1088 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IROTH;
1089 if (fInodeMode & XFS_INODE_MODE_EXEC_GROUP)
1090 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXGRP;
1091 if (fInodeMode & XFS_INODE_MODE_WRITE_GROUP)
1092 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWGRP;
1093 if (fInodeMode & XFS_INODE_MODE_READ_GROUP)
1094 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRGRP;
1095 if (fInodeMode & XFS_INODE_MODE_EXEC_OWNER)
1096 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXUSR;
1097 if (fInodeMode & XFS_INODE_MODE_WRITE_OWNER)
1098 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWUSR;
1099 if (fInodeMode & XFS_INODE_MODE_READ_OWNER)
1100 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRUSR;
1101 if (fInodeMode & XFS_INODE_MODE_STICKY)
1102 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISTXT;
1103 if (fInodeMode & XFS_INODE_MODE_SET_GROUP_ID)
1104 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISGID;
1105 if (fInodeMode & XFS_INODE_MODE_SET_USER_ID)
1106 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISUID;
1107 }
1108 }
1109 else
1110 rc = VERR_NO_MEMORY;
1111 }
1112 else
1113 {
1114 /* Remove from current LRU list position and add to the beginning. */
1115 uint32_t cRefs = ASMAtomicIncU32(&pInode->cRefs);
1116 if (cRefs == 1) /* Inodes get removed from the LRU list if they are referenced. */
1117 RTListNodeRemove(&pInode->NdLru);
1118 }
1119
1120 if (RT_SUCCESS(rc))
1121 *ppInode = pInode;
1122 else if (pInode)
1123 {
1124 ASMAtomicDecU32(&pInode->cRefs);
1125 rtFsXfsInode_Free(pThis, pInode); /* Free the inode. */
1126 }
1127
1128 return rc;
1129}
1130
1131
1132/**
1133 * Releases a reference of the given inode.
1134 *
1135 * @returns nothing.
1136 * @param pThis The XFS volume instance.
1137 * @param pInode The inode to release.
1138 */
1139static void rtFsXfsInode_Release(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode)
1140{
1141 uint32_t cRefs = ASMAtomicDecU32(&pInode->cRefs);
1142 if (!cRefs)
1143 rtFsXfsInode_Free(pThis, pInode);
1144}
1145
1146
1147/**
1148 * Worker for various QueryInfo methods.
1149 *
1150 * @returns IPRT status code.
1151 * @param pInode The inode structure to return info for.
1152 * @param pObjInfo Where to return object info.
1153 * @param enmAddAttr What additional info to return.
1154 */
1155static int rtFsXfsInode_QueryInfo(PRTFSXFSINODE pInode, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1156{
1157 RT_ZERO(*pObjInfo);
1158
1159 pObjInfo->cbObject = pInode->ObjInfo.cbObject;
1160 pObjInfo->cbAllocated = pInode->ObjInfo.cbAllocated;
1161 pObjInfo->AccessTime = pInode->ObjInfo.AccessTime;
1162 pObjInfo->ModificationTime = pInode->ObjInfo.ModificationTime;
1163 pObjInfo->ChangeTime = pInode->ObjInfo.ChangeTime;
1164 pObjInfo->BirthTime = pInode->ObjInfo.BirthTime;
1165 pObjInfo->Attr.fMode = pInode->ObjInfo.Attr.fMode;
1166 pObjInfo->Attr.enmAdditional = enmAddAttr;
1167 switch (enmAddAttr)
1168 {
1169 case RTFSOBJATTRADD_UNIX:
1170 memcpy(&pObjInfo->Attr.u.Unix, &pInode->ObjInfo.Attr.u.Unix, sizeof(pInode->ObjInfo.Attr.u.Unix));
1171 break;
1172
1173 case RTFSOBJATTRADD_UNIX_OWNER:
1174 pObjInfo->Attr.u.UnixOwner.uid = pInode->ObjInfo.Attr.u.Unix.uid;
1175 break;
1176
1177 case RTFSOBJATTRADD_UNIX_GROUP:
1178 pObjInfo->Attr.u.UnixGroup.gid = pInode->ObjInfo.Attr.u.Unix.gid;
1179 break;
1180
1181 default:
1182 break;
1183 }
1184
1185 return VINF_SUCCESS;
1186}
1187
1188
1189/**
1190 * Locates the location of the next level in the B+Tree mapping the given offset.
1191 *
1192 * @returns Filesystem block number where the next level of the B+Tree is stored.
1193 * @param paoffFile Array of file offset mappings.
1194 * @param pauFsBlock Array of filesystem block mappings.
1195 * @param cEntries Number of entries in the extent index node array.
1196 * @param iBlock The block to resolve.
1197 */
1198DECLINLINE(XFSDFSBNO) rtFsXfsInode_BTreeNdLocateNextLvl(XFSDFILOFF *paoffFile, XFSDFSBNO *pauFsBlock,
1199 uint16_t cEntries, XFSDFILOFF offFile)
1200{
1201 for (uint32_t i = 1; i < cEntries; i++)
1202 {
1203 if ( RT_BE2H_U64(paoffFile[i - 1]) <= offFile
1204 && RT_BE2H_U64(paoffFile[i]) > offFile)
1205 return RT_BE2H_U64(pauFsBlock[i]);
1206 }
1207
1208 /* Nothing found so far, the last entry must cover the block as the array is sorted. */
1209 return RT_BE2H_U64(pauFsBlock[cEntries - 1]);
1210}
1211
1212
1213/**
1214 * Locates the extent mapping the file offset in the given extents list.
1215 *
1216 * @returns IPRT status.
1217 * @param pExtents The array of extents to search.
1218 * @param cEntries Number of entries in the array.
1219 * @param uBlock The file offset to search the matching mapping for.
1220 * @param cBlocks Number of blocks requested.
1221 * @param piBlockFs Where to store the filesystem block on success.
1222 * @param pcBlocks Where to store the number of contiguous blocks on success.
1223 * @param pfSparse Where to store the sparse flag on success.
1224 */
1225DECLINLINE(int) rtFsXfsInode_ExtentLocate(PCXFSEXTENT paExtents, uint16_t cEntries, XFSDFILOFF uBlock,
1226 size_t cBlocks, uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1227{
1228 int rc = VERR_VFS_BOGUS_FORMAT;
1229
1230 for (uint32_t i = 0; i < cEntries; i++)
1231 {
1232 PCXFSEXTENT pExtent = &paExtents[i];
1233 uint64_t iBlockExtent = XFS_EXTENT_GET_LOGICAL_BLOCK(pExtent);
1234 size_t cBlocksExtent = XFS_EXTENT_GET_BLOCK_COUNT(pExtent);
1235
1236 if ( uBlock >= iBlockExtent
1237 && uBlock < iBlockExtent + cBlocksExtent)
1238 {
1239 uint64_t offExtentBlocks = uBlock - iBlockExtent;
1240 *piBlockFs = XFS_EXTENT_GET_DISK_BLOCK(pExtent) + offExtentBlocks;
1241 *pcBlocks = RT_MIN(cBlocks, cBlocksExtent - offExtentBlocks);
1242 *pfSparse = XFS_EXTENT_IS_UNWRITTEN(pExtent);
1243 rc = VINF_SUCCESS;
1244 break;
1245 }
1246 }
1247
1248 return rc;
1249}
1250
1251
1252/**
1253 * Validates the given node header.
1254 *
1255 * @returns IPRT status code.
1256 * @param pThis The XFS volume instance.
1257 * @param pNd The node header to validate.
1258 * @param iLvl The current level.
1259 */
1260static int rtFsXfsInode_BTreeNdValidate(PRTFSXFSVOL pThis, PCXFSBTREENODEHDR pNd, uint16_t iLvl)
1261{
1262 RT_NOREF(pThis, pNd, iLvl);
1263 /** @todo */
1264 return VINF_SUCCESS;
1265}
1266
1267
1268/**
1269 * Maps the given inode block to the destination filesystem block.
1270 *
1271 * @returns IPRT status code.
1272 * @param pThis The XFS volume instance.
1273 * @param pInode The inode structure to read from.
1274 * @param iBlock The inode block to map.
1275 * @param cBlocks Number of blocks requested.
1276 * @param piBlockFs Where to store the filesystem block on success.
1277 * @param pcBlocks Where to store the number of contiguous blocks on success.
1278 * @param pfSparse Where to store the sparse flag on success.
1279 *
1280 * @todo Optimize
1281 */
1282static int rtFsXfsInode_MapBlockToFs(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode, uint64_t iBlock, size_t cBlocks,
1283 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1284{
1285 int rc = VINF_SUCCESS;
1286
1287 switch (pInode->enmFormat)
1288 {
1289 case XFS_INODE_FORMAT_EXTENTS:
1290 {
1291 size_t cbRemaining = 0;
1292 PCXFSEXTENT paExtents = (PCXFSEXTENT)rtFsXfsInodeGetDataFork(pThis, pInode, &cbRemaining);
1293
1294 if (cbRemaining <= pInode->cExtentsData * sizeof(XFSEXTENT))
1295 rc = rtFsXfsInode_ExtentLocate(paExtents, pInode->cExtentsData, cBlocks, iBlock,
1296 piBlockFs, pcBlocks, pfSparse);
1297 else
1298 rc = VERR_VFS_BOGUS_FORMAT;
1299 break;
1300 }
1301 case XFS_INODE_FORMAT_BTREE:
1302 {
1303 size_t cbRemaining = 0;
1304 PCXFSBTREEROOTHDR pRoot = (PCXFSBTREEROOTHDR)rtFsXfsInodeGetDataFork(pThis, pInode, &cbRemaining);
1305 if (cbRemaining >= RT_BE2H_U16(pRoot->cRecs) * (sizeof(XFSDFSBNO) + sizeof(XFSDFILOFF)) + sizeof(XFSBTREEROOTHDR))
1306 {
1307 XFSDFILOFF *poffFile = (XFSDFILOFF *)(pRoot + 1);
1308 XFSDFSBNO *puFsBlock = (XFSDFSBNO *)(&poffFile[RT_BE2H_U16(pRoot->cRecs)]);
1309
1310 XFSDFSBNO uFsBlock = rtFsXfsInode_BTreeNdLocateNextLvl(poffFile, puFsBlock, RT_BE2H_U16(pRoot->cRecs),
1311 iBlock);
1312 uint16_t iLvl = RT_BE2H_U16(pRoot->iLvl) - 1;
1313
1314 /* Resolve intermediate levels. */
1315 while ( iLvl > 0
1316 && RT_SUCCESS(rc))
1317 {
1318 PRTFSXFSBLOCKENTRY pEntry;
1319 PCXFSBTREENODEHDR pNd;
1320
1321 rc = rtFsXfsVol_BlockLoad(pThis, uFsBlock, &pEntry, (void **)&pNd);
1322 if (RT_SUCCESS(rc))
1323 {
1324 rc = rtFsXfsInode_BTreeNdValidate(pThis, pNd, iLvl);
1325 if (RT_SUCCESS(rc))
1326 {
1327 poffFile = (XFSDFILOFF *)(pNd + 1);
1328 puFsBlock = (XFSDFSBNO *)(&poffFile[RT_BE2H_U16(pNd->cRecs)]);
1329 uFsBlock = rtFsXfsInode_BTreeNdLocateNextLvl(poffFile, puFsBlock, RT_BE2H_U16(pRoot->cRecs),
1330 iBlock);
1331 iLvl--;
1332 }
1333 rtFsXfsVol_BlockRelease(pThis, pEntry);
1334 }
1335 }
1336
1337 /* Load the leave node and parse it. */
1338 if (RT_SUCCESS(rc))
1339 {
1340 PRTFSXFSBLOCKENTRY pEntry;
1341 PCXFSBTREENODEHDR pNd;
1342
1343 rc = rtFsXfsVol_BlockLoad(pThis, uFsBlock, &pEntry, (void **)&pNd);
1344 if (RT_SUCCESS(rc))
1345 {
1346 rc = rtFsXfsInode_BTreeNdValidate(pThis, pNd, iLvl);
1347 if (RT_SUCCESS(rc))
1348 {
1349 PCXFSEXTENT paExtents = (PCXFSEXTENT)(pNd + 1);
1350 rc = rtFsXfsInode_ExtentLocate(paExtents, RT_BE2H_U16(pNd->cRecs), cBlocks, iBlock,
1351 piBlockFs, pcBlocks, pfSparse);
1352 }
1353 rtFsXfsVol_BlockRelease(pThis, pEntry);
1354 }
1355 }
1356 }
1357 else
1358 rc = VERR_VFS_BOGUS_FORMAT;
1359 break;
1360 }
1361 case XFS_INODE_FORMAT_LOCAL:
1362 case XFS_INODE_FORMAT_UUID:
1363 case XFS_INODE_FORMAT_DEV:
1364 default:
1365 rc = VERR_VFS_BOGUS_FORMAT;
1366 }
1367
1368 return rc;
1369}
1370
1371
1372/**
1373 * Reads data from the given inode at the given byte offset.
1374 *
1375 * @returns IPRT status code.
1376 * @param pThis The XFS volume instance.
1377 * @param pInode The inode structure to read from.
1378 * @param off The byte offset to start reading from.
1379 * @param pvBuf Where to store the read data to.
1380 * @param pcbRead Where to return the amount of data read.
1381 */
1382static int rtFsXfsInode_Read(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode, uint64_t off, void *pvBuf, size_t cbRead, size_t *pcbRead)
1383{
1384 int rc = VINF_SUCCESS;
1385 uint8_t *pbBuf = (uint8_t *)pvBuf;
1386
1387 if (((uint64_t)pInode->ObjInfo.cbObject < off + cbRead))
1388 {
1389 if (!pcbRead)
1390 return VERR_EOF;
1391 else
1392 cbRead = (uint64_t)pInode->ObjInfo.cbObject - off;
1393 }
1394
1395 if (pInode->enmFormat == XFS_INODE_FORMAT_LOCAL)
1396 {
1397 /* Fast path when the data is inlined in the inode. */
1398 size_t cbRemaining = 0;
1399 uint8_t *pbSrc = (uint8_t *)rtFsXfsInodeGetDataFork(pThis, pInode, &cbRemaining);
1400 if (off + cbRemaining <= (uint64_t)pInode->ObjInfo.cbObject)
1401 {
1402 memcpy(pvBuf, &pbSrc[off], cbRead);
1403 *pcbRead = cbRead;
1404 }
1405 else
1406 rc = VERR_VFS_BOGUS_FORMAT;
1407
1408 return rc;
1409 }
1410
1411 while ( cbRead
1412 && RT_SUCCESS(rc))
1413 {
1414 uint64_t iBlockStart = rtFsXfsDiskOffsetToBlockIdx(pThis, off);
1415 uint32_t offBlockStart = off % pThis->cbBlock;
1416
1417 /* Resolve the inode block to the proper filesystem block. */
1418 uint64_t iBlockFs = 0;
1419 size_t cBlocks = 0;
1420 bool fSparse = false;
1421 rc = rtFsXfsInode_MapBlockToFs(pThis, pInode, iBlockStart, 1, &iBlockFs, &cBlocks, &fSparse);
1422 if (RT_SUCCESS(rc))
1423 {
1424 Assert(cBlocks == 1);
1425
1426 size_t cbThisRead = RT_MIN(cbRead, pThis->cbBlock - offBlockStart);
1427
1428 if (!fSparse)
1429 {
1430 uint64_t offRead = rtFsXfsBlockIdxToDiskOffset(pThis, iBlockFs);
1431 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead + offBlockStart, pbBuf, cbThisRead, NULL);
1432 }
1433 else
1434 memset(pbBuf, 0, cbThisRead);
1435
1436 if (RT_SUCCESS(rc))
1437 {
1438 pbBuf += cbThisRead;
1439 cbRead -= cbThisRead;
1440 off += cbThisRead;
1441 if (pcbRead)
1442 *pcbRead += cbThisRead;
1443 }
1444 }
1445 }
1446
1447 return rc;
1448}
1449
1450
1451
1452/*
1453 *
1454 * File operations.
1455 * File operations.
1456 * File operations.
1457 *
1458 */
1459
1460/**
1461 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1462 */
1463static DECLCALLBACK(int) rtFsXfsFile_Close(void *pvThis)
1464{
1465 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1466 LogFlow(("rtFsXfsFile_Close(%p/%p)\n", pThis, pThis->pInode));
1467
1468 rtFsXfsInode_Release(pThis->pVol, pThis->pInode);
1469 pThis->pInode = NULL;
1470 pThis->pVol = NULL;
1471 return VINF_SUCCESS;
1472}
1473
1474
1475/**
1476 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1477 */
1478static DECLCALLBACK(int) rtFsXfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1479{
1480 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1481 return rtFsXfsInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
1482}
1483
1484
1485/**
1486 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1487 */
1488static DECLCALLBACK(int) rtFsXfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1489{
1490 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1491 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
1492 RT_NOREF(fBlocking);
1493
1494 if (off == -1)
1495 off = pThis->offFile;
1496 else
1497 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
1498
1499 int rc;
1500 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
1501 if (!pcbRead)
1502 {
1503 rc = rtFsXfsInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1504 if (RT_SUCCESS(rc))
1505 pThis->offFile = off + cbRead;
1506 Log6(("rtFsXfsFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
1507 }
1508 else
1509 {
1510 PRTFSXFSINODE pInode = pThis->pInode;
1511 if (off >= pInode->ObjInfo.cbObject)
1512 {
1513 *pcbRead = 0;
1514 rc = VINF_EOF;
1515 }
1516 else
1517 {
1518 if ((uint64_t)off + cbRead <= (uint64_t)pInode->ObjInfo.cbObject)
1519 rc = rtFsXfsInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1520 else
1521 {
1522 /* Return VINF_EOF if beyond end-of-file. */
1523 cbRead = (size_t)(pInode->ObjInfo.cbObject - off);
1524 rc = rtFsXfsInode_Read(pThis->pVol, pThis->pInode, off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1525 if (RT_SUCCESS(rc))
1526 rc = VINF_EOF;
1527 }
1528 if (RT_SUCCESS(rc))
1529 {
1530 pThis->offFile = off + cbRead;
1531 *pcbRead = cbRead;
1532 }
1533 else
1534 *pcbRead = 0;
1535 }
1536 Log6(("rtFsXfsFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
1537 }
1538
1539 return rc;
1540}
1541
1542
1543/**
1544 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1545 */
1546static DECLCALLBACK(int) rtFsXfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1547{
1548 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
1549 return VERR_WRITE_PROTECT;
1550}
1551
1552
1553/**
1554 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1555 */
1556static DECLCALLBACK(int) rtFsXfsFile_Flush(void *pvThis)
1557{
1558 RT_NOREF(pvThis);
1559 return VINF_SUCCESS;
1560}
1561
1562
1563/**
1564 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1565 */
1566static DECLCALLBACK(int) rtFsXfsFile_Tell(void *pvThis, PRTFOFF poffActual)
1567{
1568 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1569 *poffActual = pThis->offFile;
1570 return VINF_SUCCESS;
1571}
1572
1573
1574/**
1575 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1576 */
1577static DECLCALLBACK(int) rtFsXfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1578{
1579 RT_NOREF(pvThis, fMode, fMask);
1580 return VERR_WRITE_PROTECT;
1581}
1582
1583
1584/**
1585 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1586 */
1587static DECLCALLBACK(int) rtFsXfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1588 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1589{
1590 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1591 return VERR_WRITE_PROTECT;
1592}
1593
1594
1595/**
1596 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1597 */
1598static DECLCALLBACK(int) rtFsXfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1599{
1600 RT_NOREF(pvThis, uid, gid);
1601 return VERR_WRITE_PROTECT;
1602}
1603
1604
1605/**
1606 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1607 */
1608static DECLCALLBACK(int) rtFsXfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1609{
1610 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1611 RTFOFF offNew;
1612 switch (uMethod)
1613 {
1614 case RTFILE_SEEK_BEGIN:
1615 offNew = offSeek;
1616 break;
1617 case RTFILE_SEEK_END:
1618 offNew = pThis->pInode->ObjInfo.cbObject + offSeek;
1619 break;
1620 case RTFILE_SEEK_CURRENT:
1621 offNew = (RTFOFF)pThis->offFile + offSeek;
1622 break;
1623 default:
1624 return VERR_INVALID_PARAMETER;
1625 }
1626 if (offNew >= 0)
1627 {
1628 pThis->offFile = offNew;
1629 *poffActual = offNew;
1630 return VINF_SUCCESS;
1631 }
1632 return VERR_NEGATIVE_SEEK;
1633}
1634
1635
1636/**
1637 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1638 */
1639static DECLCALLBACK(int) rtFsXfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1640{
1641 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1642 *pcbFile = (uint64_t)pThis->pInode->ObjInfo.cbObject;
1643 return VINF_SUCCESS;
1644}
1645
1646
1647/**
1648 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
1649 */
1650static DECLCALLBACK(int) rtFsXfsFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
1651{
1652 RT_NOREF(pvThis, cbFile, fFlags);
1653 return VERR_WRITE_PROTECT;
1654}
1655
1656
1657/**
1658 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
1659 */
1660static DECLCALLBACK(int) rtFsXfsFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
1661{
1662 RT_NOREF(pvThis);
1663 *pcbMax = INT64_MAX; /** @todo */
1664 return VINF_SUCCESS;
1665}
1666
1667
1668/**
1669 * XFS file operations.
1670 */
1671static const RTVFSFILEOPS g_rtFsXfsFileOps =
1672{
1673 { /* Stream */
1674 { /* Obj */
1675 RTVFSOBJOPS_VERSION,
1676 RTVFSOBJTYPE_FILE,
1677 "XFS File",
1678 rtFsXfsFile_Close,
1679 rtFsXfsFile_QueryInfo,
1680 NULL,
1681 RTVFSOBJOPS_VERSION
1682 },
1683 RTVFSIOSTREAMOPS_VERSION,
1684 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1685 rtFsXfsFile_Read,
1686 rtFsXfsFile_Write,
1687 rtFsXfsFile_Flush,
1688 NULL /*PollOne*/,
1689 rtFsXfsFile_Tell,
1690 NULL /*pfnSkip*/,
1691 NULL /*pfnZeroFill*/,
1692 RTVFSIOSTREAMOPS_VERSION,
1693 },
1694 RTVFSFILEOPS_VERSION,
1695 0,
1696 { /* ObjSet */
1697 RTVFSOBJSETOPS_VERSION,
1698 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1699 rtFsXfsFile_SetMode,
1700 rtFsXfsFile_SetTimes,
1701 rtFsXfsFile_SetOwner,
1702 RTVFSOBJSETOPS_VERSION
1703 },
1704 rtFsXfsFile_Seek,
1705 rtFsXfsFile_QuerySize,
1706 rtFsXfsFile_SetSize,
1707 rtFsXfsFile_QueryMaxSize,
1708 RTVFSFILEOPS_VERSION
1709};
1710
1711
1712/**
1713 * Creates a new VFS file from the given regular file inode.
1714 *
1715 * @returns IPRT status code.
1716 * @param pThis The XFS volume instance.
1717 * @param fOpen Open flags passed.
1718 * @param iInode The inode for the file.
1719 * @param phVfsFile Where to store the VFS file handle on success.
1720 * @param pErrInfo Where to record additional error information on error, optional.
1721 * @param pszWhat Logging prefix.
1722 */
1723static int rtFsXfsVol_NewFile(PRTFSXFSVOL pThis, uint64_t fOpen, uint32_t iInode,
1724 PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo, const char *pszWhat)
1725{
1726 /*
1727 * Load the inode and check that it really is a file.
1728 */
1729 PRTFSXFSINODE pInode = NULL;
1730 int rc = rtFsXfsInode_Load(pThis, iInode, &pInode);
1731 if (RT_SUCCESS(rc))
1732 {
1733 if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
1734 {
1735 PRTFSXFSFILE pNewFile;
1736 rc = RTVfsNewFile(&g_rtFsXfsFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
1737 phVfsFile, (void **)&pNewFile);
1738 if (RT_SUCCESS(rc))
1739 {
1740 pNewFile->pVol = pThis;
1741 pNewFile->pInode = pInode;
1742 pNewFile->offFile = 0;
1743 }
1744 }
1745 else
1746 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: fMode=%#RX32", pszWhat, pInode->ObjInfo.Attr.fMode);
1747
1748 if (RT_FAILURE(rc))
1749 rtFsXfsInode_Release(pThis, pInode);
1750 }
1751
1752 return rc;
1753}
1754
1755
1756
1757/*
1758 *
1759 * XFS directory code.
1760 * XFS directory code.
1761 * XFS directory code.
1762 *
1763 */
1764
1765/**
1766 * Looks up an entry in the given directory inode.
1767 *
1768 * @returns IPRT status code.
1769 * @param pThis The XFS volume instance.
1770 * @param pInode The directory inode structure to.
1771 * @param pszEntry The entry to lookup.
1772 * @param piInode Where to store the inode number if the entry was found.
1773 */
1774static int rtFsXfsDir_Lookup(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode, const char *pszEntry, uint32_t *piInode)
1775{
1776 uint64_t offEntry = 0;
1777 int rc = VERR_FILE_NOT_FOUND;
1778 uint32_t idxDirEntry = 0;
1779 size_t cchEntry = strlen(pszEntry);
1780
1781 if (cchEntry > 255)
1782 return VERR_FILENAME_TOO_LONG;
1783
1784 RT_NOREF(pThis, idxDirEntry, offEntry, pInode, piInode);
1785
1786#if 0 /** @todo */
1787 while (offEntry < (uint64_t)pInode->ObjInfo.cbObject)
1788 {
1789 EXTDIRENTRYEX DirEntry;
1790 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - offEntry);
1791 int rc2 = rtFsXfsInode_Read(pThis, pInode, offEntry, &DirEntry, cbThis, NULL);
1792 if (RT_SUCCESS(rc2))
1793 {
1794#ifdef LOG_ENABLED
1795 rtFsExtDirEntry_Log(pThis, idxDirEntry, &DirEntry);
1796#endif
1797
1798 uint16_t cbName = pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
1799 ? DirEntry.Core.u.v2.cbName
1800 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
1801 if ( cchEntry == cbName
1802 && !memcmp(pszEntry, &DirEntry.Core.achName[0], cchEntry))
1803 {
1804 *piInode = RT_LE2H_U32(DirEntry.Core.iInodeRef);
1805 rc = VINF_SUCCESS;
1806 break;
1807 }
1808
1809 offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
1810 idxDirEntry++;
1811 }
1812 else
1813 {
1814 rc = rc2;
1815 break;
1816 }
1817 }
1818#endif
1819 return rc;
1820}
1821
1822
1823
1824/*
1825 *
1826 * Directory instance methods
1827 * Directory instance methods
1828 * Directory instance methods
1829 *
1830 */
1831
1832/**
1833 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1834 */
1835static DECLCALLBACK(int) rtFsXfsDir_Close(void *pvThis)
1836{
1837 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1838 LogFlowFunc(("pThis=%p\n", pThis));
1839 rtFsXfsInode_Release(pThis->pVol, pThis->pInode);
1840 pThis->pInode = NULL;
1841 return VINF_SUCCESS;
1842}
1843
1844
1845/**
1846 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1847 */
1848static DECLCALLBACK(int) rtFsXfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1849{
1850 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1851 LogFlowFunc(("\n"));
1852 return rtFsXfsInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
1853}
1854
1855
1856/**
1857 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1858 */
1859static DECLCALLBACK(int) rtFsXfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1860{
1861 LogFlowFunc(("\n"));
1862 RT_NOREF(pvThis, fMode, fMask);
1863 return VERR_WRITE_PROTECT;
1864}
1865
1866
1867/**
1868 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1869 */
1870static DECLCALLBACK(int) rtFsXfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1871 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1872{
1873 LogFlowFunc(("\n"));
1874 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1875 return VERR_WRITE_PROTECT;
1876}
1877
1878
1879/**
1880 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1881 */
1882static DECLCALLBACK(int) rtFsXfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1883{
1884 LogFlowFunc(("\n"));
1885 RT_NOREF(pvThis, uid, gid);
1886 return VERR_WRITE_PROTECT;
1887}
1888
1889
1890/**
1891 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
1892 */
1893static DECLCALLBACK(int) rtFsXfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
1894 uint32_t fFlags, PRTVFSOBJ phVfsObj)
1895{
1896 LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
1897 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1898 PRTFSXFSVOL pVol = pThis->pVol;
1899 int rc = VINF_SUCCESS;
1900
1901 RT_NOREF(pThis, pVol, phVfsObj, pszEntry, fFlags);
1902
1903 /*
1904 * We cannot create or replace anything, just open stuff.
1905 */
1906 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
1907 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
1908 { /* likely */ }
1909 else
1910 return VERR_WRITE_PROTECT;
1911
1912 /*
1913 * Lookup the entry.
1914 */
1915 uint32_t iInode = 0;
1916 rc = rtFsXfsDir_Lookup(pVol, pThis->pInode, pszEntry, &iInode);
1917 if (RT_SUCCESS(rc))
1918 {
1919 PRTFSXFSINODE pInode = NULL;
1920 rc = rtFsXfsInode_Load(pVol, iInode, &pInode);
1921 if (RT_SUCCESS(rc))
1922 {
1923 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
1924 {
1925 RTVFSDIR hVfsDir;
1926 rc = rtFsXfsVol_OpenDirByInode(pVol, iInode, &hVfsDir);
1927 if (RT_SUCCESS(rc))
1928 {
1929 *phVfsObj = RTVfsObjFromDir(hVfsDir);
1930 RTVfsDirRelease(hVfsDir);
1931 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
1932 }
1933 }
1934 else if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
1935 {
1936 RTVFSFILE hVfsFile;
1937 rc = rtFsXfsVol_NewFile(pVol, fOpen, iInode, &hVfsFile, NULL, pszEntry);
1938 if (RT_SUCCESS(rc))
1939 {
1940 *phVfsObj = RTVfsObjFromFile(hVfsFile);
1941 RTVfsFileRelease(hVfsFile);
1942 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
1943 }
1944 }
1945 else
1946 rc = VERR_NOT_SUPPORTED;
1947 }
1948 }
1949
1950 LogFlow(("rtFsXfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
1951 return rc;
1952}
1953
1954
1955/**
1956 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
1957 */
1958static DECLCALLBACK(int) rtFsXfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
1959{
1960 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
1961 LogFlowFunc(("\n"));
1962 return VERR_WRITE_PROTECT;
1963}
1964
1965
1966/**
1967 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
1968 */
1969static DECLCALLBACK(int) rtFsXfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
1970{
1971 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
1972 LogFlowFunc(("\n"));
1973 return VERR_NOT_SUPPORTED;
1974}
1975
1976
1977/**
1978 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
1979 */
1980static DECLCALLBACK(int) rtFsXfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
1981 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
1982{
1983 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
1984 LogFlowFunc(("\n"));
1985 return VERR_WRITE_PROTECT;
1986}
1987
1988
1989/**
1990 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
1991 */
1992static DECLCALLBACK(int) rtFsXfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
1993{
1994 RT_NOREF(pvThis, pszEntry, fType);
1995 LogFlowFunc(("\n"));
1996 return VERR_WRITE_PROTECT;
1997}
1998
1999
2000/**
2001 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
2002 */
2003static DECLCALLBACK(int) rtFsXfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
2004{
2005 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
2006 LogFlowFunc(("\n"));
2007 return VERR_WRITE_PROTECT;
2008}
2009
2010
2011/**
2012 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
2013 */
2014static DECLCALLBACK(int) rtFsXfsDir_RewindDir(void *pvThis)
2015{
2016 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
2017 LogFlowFunc(("\n"));
2018
2019 pThis->fNoMoreFiles = false;
2020 pThis->offEntry = 0;
2021 pThis->idxEntry = 0;
2022 return VINF_SUCCESS;
2023}
2024
2025
2026/**
2027 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
2028 */
2029static DECLCALLBACK(int) rtFsXfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
2030 RTFSOBJATTRADD enmAddAttr)
2031{
2032 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
2033 PRTFSXFSINODE pInode = pThis->pInode;
2034 LogFlowFunc(("\n"));
2035
2036 if (pThis->fNoMoreFiles)
2037 return VERR_NO_MORE_FILES;
2038
2039 RT_NOREF(pInode, pDirEntry, pcbDirEntry, enmAddAttr);
2040 return VERR_NOT_IMPLEMENTED;
2041}
2042
2043
2044/**
2045 * XFS directory operations.
2046 */
2047static const RTVFSDIROPS g_rtFsXfsDirOps =
2048{
2049 { /* Obj */
2050 RTVFSOBJOPS_VERSION,
2051 RTVFSOBJTYPE_DIR,
2052 "XFS Dir",
2053 rtFsXfsDir_Close,
2054 rtFsXfsDir_QueryInfo,
2055 NULL,
2056 RTVFSOBJOPS_VERSION
2057 },
2058 RTVFSDIROPS_VERSION,
2059 0,
2060 { /* ObjSet */
2061 RTVFSOBJSETOPS_VERSION,
2062 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
2063 rtFsXfsDir_SetMode,
2064 rtFsXfsDir_SetTimes,
2065 rtFsXfsDir_SetOwner,
2066 RTVFSOBJSETOPS_VERSION
2067 },
2068 rtFsXfsDir_Open,
2069 NULL /* pfnFollowAbsoluteSymlink */,
2070 NULL /* pfnOpenFile */,
2071 NULL /* pfnOpenDir */,
2072 rtFsXfsDir_CreateDir,
2073 rtFsXfsDir_OpenSymlink,
2074 rtFsXfsDir_CreateSymlink,
2075 NULL /* pfnQueryEntryInfo */,
2076 rtFsXfsDir_UnlinkEntry,
2077 rtFsXfsDir_RenameEntry,
2078 rtFsXfsDir_RewindDir,
2079 rtFsXfsDir_ReadDir,
2080 RTVFSDIROPS_VERSION,
2081};
2082
2083
2084/**
2085 * Opens a directory by the given inode.
2086 *
2087 * @returns IPRT status code.
2088 * @param pThis The XFS volume instance.
2089 * @param iInode The inode to open.
2090 * @param phVfsDir Where to store the handle to the VFS directory on success.
2091 */
2092static int rtFsXfsVol_OpenDirByInode(PRTFSXFSVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir)
2093{
2094 PRTFSXFSINODE pInode = NULL;
2095 int rc = rtFsXfsInode_Load(pThis, iInode, &pInode);
2096 if (RT_SUCCESS(rc))
2097 {
2098 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
2099 {
2100 PRTFSXFSDIR pNewDir;
2101 rc = RTVfsNewDir(&g_rtFsXfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
2102 phVfsDir, (void **)&pNewDir);
2103 if (RT_SUCCESS(rc))
2104 {
2105 pNewDir->fNoMoreFiles = false;
2106 pNewDir->pVol = pThis;
2107 pNewDir->pInode = pInode;
2108 }
2109 }
2110 else
2111 rc = VERR_VFS_BOGUS_FORMAT;
2112
2113 if (RT_FAILURE(rc))
2114 rtFsXfsInode_Release(pThis, pInode);
2115 }
2116
2117 return rc;
2118}
2119
2120
2121
2122/*
2123 *
2124 * Volume level code.
2125 * Volume level code.
2126 * Volume level code.
2127 *
2128 */
2129
2130static DECLCALLBACK(int) rtFsXfsVolAgTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
2131{
2132 RT_NOREF(pvUser);
2133
2134 PRTFSXFSAG pAg = (PRTFSXFSAG)pCore;
2135 Assert(!pAg->cRefs);
2136 RTMemFree(pAg);
2137 return VINF_SUCCESS;
2138}
2139
2140
2141static DECLCALLBACK(int) rtFsXfsVolInodeTreeDestroy(PAVLU64NODECORE pCore, void *pvUser)
2142{
2143 RT_NOREF(pvUser);
2144
2145 PRTFSXFSINODE pInode = (PRTFSXFSINODE)pCore;
2146 Assert(!pInode->cRefs);
2147 RTMemFree(pInode);
2148 return VINF_SUCCESS;
2149}
2150
2151
2152static DECLCALLBACK(int) rtFsXfsVolBlockTreeDestroy(PAVLU64NODECORE pCore, void *pvUser)
2153{
2154 RT_NOREF(pvUser);
2155
2156 PRTFSXFSBLOCKENTRY pBlock = (PRTFSXFSBLOCKENTRY)pCore;
2157 Assert(!pBlock->cRefs);
2158 RTMemFree(pBlock);
2159 return VINF_SUCCESS;
2160}
2161
2162
2163/**
2164 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
2165 */
2166static DECLCALLBACK(int) rtFsXfsVol_Close(void *pvThis)
2167{
2168 PRTFSXFSVOL pThis = (PRTFSXFSVOL)pvThis;
2169
2170 /* Destroy the block group tree. */
2171 RTAvlU32Destroy(&pThis->AgRoot, rtFsXfsVolAgTreeDestroy, pThis);
2172 pThis->AgRoot = NULL;
2173 RTListInit(&pThis->LstAgLru);
2174
2175 /* Destroy the inode tree. */
2176 RTAvlU64Destroy(&pThis->InodeRoot, rtFsXfsVolInodeTreeDestroy, pThis);
2177 pThis->InodeRoot = NULL;
2178 RTListInit(&pThis->LstInodeLru);
2179
2180 /* Destroy the block cache tree. */
2181 RTAvlU64Destroy(&pThis->BlockRoot, rtFsXfsVolBlockTreeDestroy, pThis);
2182 pThis->BlockRoot = NULL;
2183 RTListInit(&pThis->LstBlockLru);
2184
2185 /*
2186 * Backing file and handles.
2187 */
2188 RTVfsFileRelease(pThis->hVfsBacking);
2189 pThis->hVfsBacking = NIL_RTVFSFILE;
2190 pThis->hVfsSelf = NIL_RTVFS;
2191
2192 return VINF_SUCCESS;
2193}
2194
2195
2196/**
2197 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
2198 */
2199static DECLCALLBACK(int) rtFsXfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2200{
2201 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
2202 return VERR_WRONG_TYPE;
2203}
2204
2205
2206/**
2207 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
2208 */
2209static DECLCALLBACK(int) rtFsXfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
2210{
2211 PRTFSXFSVOL pThis = (PRTFSXFSVOL)pvThis;
2212 int rc = rtFsXfsVol_OpenDirByInode(pThis, pThis->uInodeRoot, phVfsDir);
2213 LogFlowFunc(("returns %Rrc\n", rc));
2214 return rc;
2215}
2216
2217
2218/**
2219 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
2220 */
2221static DECLCALLBACK(int) rtFsXfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
2222{
2223 RT_NOREF(pvThis, off, cb, pfUsed);
2224 return VERR_NOT_IMPLEMENTED;
2225}
2226
2227
2228DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsXfsVolOps =
2229{
2230 /* .Obj = */
2231 {
2232 /* .uVersion = */ RTVFSOBJOPS_VERSION,
2233 /* .enmType = */ RTVFSOBJTYPE_VFS,
2234 /* .pszName = */ "XfsVol",
2235 /* .pfnClose = */ rtFsXfsVol_Close,
2236 /* .pfnQueryInfo = */ rtFsXfsVol_QueryInfo,
2237 /* .pfnQueryInfoEx = */ NULL,
2238 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
2239 },
2240 /* .uVersion = */ RTVFSOPS_VERSION,
2241 /* .fFeatures = */ 0,
2242 /* .pfnOpenRoot = */ rtFsXfsVol_OpenRoot,
2243 /* .pfnQueryRangeState = */ rtFsXfsVol_QueryRangeState,
2244 /* .uEndMarker = */ RTVFSOPS_VERSION
2245};
2246
2247
2248/**
2249 * Loads and parses the AGI block.
2250 *
2251 * @returns IPRT status code.
2252 * @param pThis The XFS volume instance.
2253 * @param pErrInfo Where to return additional error info.
2254 */
2255static int rtFsXfsVolLoadAgi(PRTFSXFSVOL pThis, PRTERRINFO pErrInfo)
2256{
2257 XFSAGI Agi;
2258 int rc = RTVfsFileReadAt(pThis->hVfsBacking, 2 * pThis->cbSector, &Agi, sizeof(&Agi), NULL);
2259 if (RT_SUCCESS(rc))
2260 {
2261#ifdef LOG_ENABLED
2262 rtFsXfsAgi_Log(0, &Agi);
2263#endif
2264
2265 /** @todo Verification */
2266 RT_NOREF(pErrInfo);
2267 }
2268
2269 return rc;
2270}
2271
2272
2273/**
2274 * Loads and parses the superblock of the filesystem.
2275 *
2276 * @returns IPRT status code.
2277 * @param pThis The XFS volume instance.
2278 * @param pErrInfo Where to return additional error info.
2279 */
2280static int rtFsXfsVolLoadAndParseSuperblock(PRTFSXFSVOL pThis, PRTERRINFO pErrInfo)
2281{
2282 int rc = VINF_SUCCESS;
2283 XFSSUPERBLOCK Sb;
2284 rc = RTVfsFileReadAt(pThis->hVfsBacking, XFS_SB_OFFSET, &Sb, sizeof(XFSSUPERBLOCK), NULL);
2285 if (RT_FAILURE(rc))
2286 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
2287
2288 /* Validate the superblock. */
2289 if (RT_BE2H_U32(Sb.u32Magic) != XFS_SB_MAGIC)
2290 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not XFS - Signature mismatch: %RX32", RT_BE2H_U32(Sb.u32Magic));
2291
2292#ifdef LOG_ENABLED
2293 rtFsXfsSb_Log(0, &Sb);
2294#endif
2295
2296 /** @todo More verification */
2297 pThis->cbSector = RT_BE2H_U32(Sb.cbSector);
2298 pThis->cbBlock = RT_BE2H_U32(Sb.cbBlock);
2299 pThis->cBlockShift = Sb.cBlockSzLog;
2300 pThis->cBlocksPerAg = RT_BE2H_U32(Sb.cAgBlocks);
2301 pThis->cAgs = RT_BE2H_U32(Sb.cAg);
2302 pThis->uInodeRoot = RT_BE2H_U64(Sb.uInodeRoot);
2303 pThis->cbInode = RT_BE2H_U16(Sb.cbInode);
2304 pThis->cInodesPerBlock = RT_BE2H_U16(Sb.cInodesPerBlock);
2305 pThis->cAgBlocksLog = Sb.cAgBlocksLog;
2306 pThis->cInodesPerBlockLog = Sb.cInodesPerBlockLog;
2307 return rc;
2308}
2309
2310
2311RTDECL(int) RTFsXfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fXfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
2312{
2313 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
2314 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
2315 AssertReturn(!fXfsFlags, VERR_INVALID_FLAGS);
2316
2317 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
2318 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2319
2320 /*
2321 * Create a VFS instance and initialize the data so rtFsXfsVol_Close works.
2322 */
2323 RTVFS hVfs;
2324 PRTFSXFSVOL pThis;
2325 int rc = RTVfsNew(&g_rtFsXfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
2326 if (RT_SUCCESS(rc))
2327 {
2328 pThis->hVfsBacking = hVfsFileIn;
2329 pThis->hVfsSelf = hVfs;
2330 pThis->fMntFlags = fMntFlags;
2331 pThis->fXfsFlags = fXfsFlags;
2332 pThis->AgRoot = NULL;
2333 pThis->InodeRoot = NULL;
2334 pThis->BlockRoot = NULL;
2335 pThis->cbAgs = 0;
2336 pThis->cbInodes = 0;
2337 pThis->cbBlocks = 0;
2338 RTListInit(&pThis->LstAgLru);
2339 RTListInit(&pThis->LstInodeLru);
2340 RTListInit(&pThis->LstBlockLru);
2341
2342 rc = RTVfsFileQuerySize(pThis->hVfsBacking, &pThis->cbBacking);
2343 if (RT_SUCCESS(rc))
2344 {
2345 rc = rtFsXfsVolLoadAndParseSuperblock(pThis, pErrInfo);
2346 if (RT_SUCCESS(rc))
2347 rc = rtFsXfsVolLoadAgi(pThis, pErrInfo);
2348 if (RT_SUCCESS(rc))
2349 {
2350 *phVfs = hVfs;
2351 return VINF_SUCCESS;
2352 }
2353 }
2354
2355 RTVfsRelease(hVfs);
2356 *phVfs = NIL_RTVFS;
2357 }
2358 else
2359 RTVfsFileRelease(hVfsFileIn);
2360
2361 return rc;
2362}
2363
2364
2365/**
2366 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
2367 */
2368static DECLCALLBACK(int) rtVfsChainXfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
2369 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
2370{
2371 RT_NOREF(pProviderReg);
2372
2373 /*
2374 * Basic checks.
2375 */
2376 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
2377 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
2378 if ( pElement->enmType != RTVFSOBJTYPE_VFS
2379 && pElement->enmType != RTVFSOBJTYPE_DIR)
2380 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
2381 if (pElement->cArgs > 1)
2382 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
2383
2384 /*
2385 * Parse the flag if present, save in pElement->uProvider.
2386 */
2387 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
2388 if (pElement->cArgs > 0)
2389 {
2390 const char *psz = pElement->paArgs[0].psz;
2391 if (*psz)
2392 {
2393 if (!strcmp(psz, "ro"))
2394 fReadOnly = true;
2395 else if (!strcmp(psz, "rw"))
2396 fReadOnly = false;
2397 else
2398 {
2399 *poffError = pElement->paArgs[0].offSpec;
2400 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
2401 }
2402 }
2403 }
2404
2405 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
2406 return VINF_SUCCESS;
2407}
2408
2409
2410/**
2411 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2412 */
2413static DECLCALLBACK(int) rtVfsChainXfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2414 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2415 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2416{
2417 RT_NOREF(pProviderReg, pSpec, poffError);
2418
2419 int rc;
2420 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2421 if (hVfsFileIn != NIL_RTVFSFILE)
2422 {
2423 RTVFS hVfs;
2424 rc = RTFsXfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
2425 RTVfsFileRelease(hVfsFileIn);
2426 if (RT_SUCCESS(rc))
2427 {
2428 *phVfsObj = RTVfsObjFromVfs(hVfs);
2429 RTVfsRelease(hVfs);
2430 if (*phVfsObj != NIL_RTVFSOBJ)
2431 return VINF_SUCCESS;
2432 rc = VERR_VFS_CHAIN_CAST_FAILED;
2433 }
2434 }
2435 else
2436 rc = VERR_VFS_CHAIN_CAST_FAILED;
2437 return rc;
2438}
2439
2440
2441/**
2442 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2443 */
2444static DECLCALLBACK(bool) rtVfsChainXfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2445 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2446 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2447{
2448 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2449 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2450 || !pReuseElement->paArgs[0].uProvider)
2451 return true;
2452 return false;
2453}
2454
2455
2456/** VFS chain element 'xfs'. */
2457static RTVFSCHAINELEMENTREG g_rtVfsChainXfsVolReg =
2458{
2459 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2460 /* fReserved = */ 0,
2461 /* pszName = */ "xfs",
2462 /* ListEntry = */ { NULL, NULL },
2463 /* pszHelp = */ "Open a XFS file system, requires a file object on the left side.\n"
2464 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
2465 /* pfnValidate = */ rtVfsChainXfsVol_Validate,
2466 /* pfnInstantiate = */ rtVfsChainXfsVol_Instantiate,
2467 /* pfnCanReuseElement = */ rtVfsChainXfsVol_CanReuseElement,
2468 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2469};
2470
2471RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainXfsVolReg, rtVfsChainXfsVolReg);
2472
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette