VirtualBox

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

Last change on this file since 82660 was 80585, checked in by vboxsync, 5 years ago

Runtime: Some renaming to stay consistent (*Get* always returns what is asked for while *Query* returns a status code and where to store the value on success is given as a pointer)

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