VirtualBox

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

Last change on this file since 76616 was 76616, checked in by vboxsync, 6 years ago

Runtime/VFS: Started XFS filesystem parser (not built, committed for backup purposes)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.0 KB
Line 
1/* $Id: xfsvfs.cpp 76616 2019-01-02 19:46:01Z vboxsync $ */
2/** @file
3 * IPRT - XFS Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2018 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 AVLU32NODECORE 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 /** @todo */
111} RTFSXFSINODE;
112/** Pointer to an in-memory inode. */
113typedef RTFSXFSINODE *PRTFSXFSINODE;
114
115
116/**
117 * Block cache entry.
118 */
119typedef struct RTFSXFSBLOCKENTRY
120{
121 /** AVL tree node, indexed by the filesystem block number. */
122 AVLU64NODECORE Core;
123 /** List node for the inode LRU list used for eviction. */
124 RTLISTNODE NdLru;
125 /** Reference counter. */
126 volatile uint32_t cRefs;
127 /** The block data. */
128 uint8_t abData[1];
129} RTFSXFSBLOCKENTRY;
130/** Pointer to a block cache entry. */
131typedef RTFSXFSBLOCKENTRY *PRTFSXFSBLOCKENTRY;
132
133
134/**
135 * Open directory instance.
136 */
137typedef struct RTFSXFSDIR
138{
139 /** Volume this directory belongs to. */
140 PRTFSXFSVOL pVol;
141 /** The underlying inode structure. */
142 PRTFSXFSINODE pInode;
143 /** Set if we've reached the end of the directory enumeration. */
144 bool fNoMoreFiles;
145 /** Current offset into the directory where the next entry should be read. */
146 uint64_t offEntry;
147 /** Next entry index (for logging purposes). */
148 uint32_t idxEntry;
149} RTFSXFSDIR;
150/** Pointer to an open directory instance. */
151typedef RTFSXFSDIR *PRTFSXFSDIR;
152
153
154/**
155 * Open file instance.
156 */
157typedef struct RTFSXFSFILE
158{
159 /** Volume this directory belongs to. */
160 PRTFSXFSVOL pVol;
161 /** The underlying inode structure. */
162 PRTFSXFSINODE pInode;
163 /** Current offset into the file for I/O. */
164 RTFOFF offFile;
165} RTFSXFSFILE;
166/** Pointer to an open file instance. */
167typedef RTFSXFSFILE *PRTFSXFSFILE;
168
169
170/**
171 * XFS filesystem volume.
172 */
173typedef struct RTFSXFSVOL
174{
175 /** Handle to itself. */
176 RTVFS hVfsSelf;
177 /** The file, partition, or whatever backing the ext volume. */
178 RTVFSFILE hVfsBacking;
179 /** The size of the backing thingy. */
180 uint64_t cbBacking;
181
182 /** RTVFSMNT_F_XXX. */
183 uint32_t fMntFlags;
184 /** RTFSXFSVFS_F_XXX (currently none defined). */
185 uint32_t fXfsFlags;
186
187 /** Size of one block. */
188 size_t cbBlock;
189 /** Number of bits to shift for converting a block number to byte offset. */
190 uint32_t cBlockShift;
191 /** Number of blocks per allocation group. */
192 XFSAGNUMBER cBlocksPerAg;
193
194 /** @name Allocation group cache.
195 * @{ */
196 /** LRU list anchor. */
197 RTLISTANCHOR LstAgLru;
198 /** Root of the cached allocation group tree. */
199 AVLU32TREE AgRoot;
200 /** Size of the cached allocation groups. */
201 size_t cbAgs;
202 /** @} */
203
204 /** @name Inode cache.
205 * @{ */
206 /** LRU list anchor for the inode cache. */
207 RTLISTANCHOR LstInodeLru;
208 /** Root of the cached inode tree. */
209 AVLU32TREE InodeRoot;
210 /** Size of the cached inodes. */
211 size_t cbInodes;
212 /** @} */
213
214 /** @name Block cache.
215 * @{ */
216 /** LRU list anchor for the block cache. */
217 RTLISTANCHOR LstBlockLru;
218 /** Root of the cached block tree. */
219 AVLU64TREE BlockRoot;
220 /** Size of cached blocks. */
221 size_t cbBlocks;
222 /** @} */
223} RTFSXFSVOL;
224
225
226
227/*********************************************************************************************************************************
228* Internal Functions *
229*********************************************************************************************************************************/
230static int rtFsXfsVol_OpenDirByInode(PRTFSXFSVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir);
231
232#ifdef LOG_ENABLED
233/**
234 * Logs the XFS filesystem superblock.
235 *
236 * @returns nothing.
237 * @param pSb Pointer to the superblock.
238 */
239static void rtFsXfsSb_Log(PCXFSSUPERBLOCK pSb)
240{
241 if (LogIs2Enabled())
242 {
243 Log2(("XFS: Superblock:\n"));
244 Log2(("XFS: u32Magic %#RX32\n", RT_BE2H_U32(pSb->u32Magic)));
245 Log2(("XFS: cbBlock %RU32\n", RT_BE2H_U32(pSb->cbBlock)));
246 Log2(("XFS: cBlocks %RU64\n", RT_BE2H_U64(pSb->cBlocks)));
247 Log2(("XFS: cBlocksRtDev %RU64\n", RT_BE2H_U64(pSb->cBlocksRtDev)));
248 Log2(("XFS: cExtentsRtDev %RU64\n", RT_BE2H_U64(pSb->cExtentsRtDev)));
249 Log2(("XFS: abUuid <todo>\n"));
250 Log2(("XFS: uBlockJournal %#RX64\n", RT_BE2H_U64(pSb->uBlockJournal)));
251 Log2(("XFS: uInodeRoot %#RX64\n", RT_BE2H_U64(pSb->uInodeRoot)));
252 Log2(("XFS: uInodeBitmapRtExt %#RX64\n", RT_BE2H_U64(pSb->uInodeBitmapRtExt)));
253 Log2(("XFS: uInodeBitmapSummary %#RX64\n", RT_BE2H_U64(pSb->uInodeBitmapSummary)));
254 Log2(("XFS: cRtExtent %RU32\n", RT_BE2H_U32(pSb->cRtExtent)));
255 Log2(("XFS: cAgBlocks %RU32\n", RT_BE2H_U32(pSb->cAgBlocks)));
256 Log2(("XFS: cAg %RU32\n", RT_BE2H_U32(pSb->cAg)));
257 Log2(("XFS: cRtBitmapBlocks %RU32\n", RT_BE2H_U32(pSb->cRtBitmapBlocks)));
258 Log2(("XFS: cJournalBlocks %RU32\n", RT_BE2H_U32(pSb->cJournalBlocks)));
259 Log2(("XFS: fVersion %#RX16%s%s%s%s%s%s%s%s%s%s%s\n", RT_BE2H_U16(pSb->fVersion),
260 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_ATTR ? " attr" : "",
261 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_NLINK ? " nlink" : "",
262 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_QUOTA ? " quota" : "",
263 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_ALIGN ? " align" : "",
264 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_DALIGN ? " dalign" : "",
265 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_SHARED ? " shared" : "",
266 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_LOGV2 ? " logv2" : "",
267 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_SECTOR ? " sector" : "",
268 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_EXTFLG ? " extflg" : "",
269 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_DIRV2 ? " dirv2" : "",
270 RT_BE2H_U16(pSb->fVersion) & XFS_SB_VERSION_F_FEAT2 ? " feat2" : ""));
271 Log2(("XFS: cbSector %RU16\n", RT_BE2H_U16(pSb->cbSector)));
272 Log2(("XFS: cbInode %RU16\n", RT_BE2H_U16(pSb->cbInode)));
273 Log2(("XFS: cIndoesPerBlock %RU16\n", RT_BE2H_U16(pSb->cInodesPerBlock)));
274 Log2(("XFS: achFsName %12s\n", &pSb->achFsName[0]));
275 Log2(("XFS: cBlockSzLog %RU8\n", pSb->cBlockSzLog));
276 Log2(("XFS: cSectorSzLog %RU8\n", pSb->cSectorSzLog));
277 Log2(("XFS: cInodeSzLog %RU8\n", pSb->cInodeSzLog));
278 Log2(("XFS: cInodesPerBlockLog %RU8\n", pSb->cInodesPerBlockLog));
279 Log2(("XFS: cAgBlocksLog %RU8\n", pSb->cAgBlocksLog));
280 Log2(("XFS: cExtentsRtDevLog %RU8\n", pSb->cExtentsRtDevLog));
281 Log2(("XFS: fInProgress %RU8\n", pSb->fInProgress));
282 Log2(("XFS: cInodeMaxPct %RU8\n", pSb->cInodeMaxPct));
283 Log2(("XFS: cInodesGlobal %#RX64\n", RT_BE2H_U64(pSb->cInodesGlobal)));
284 Log2(("XFS: cInodesGlobalFree %#RX64\n", RT_BE2H_U64(pSb->cInodesGlobalFree)));
285 Log2(("XFS: cBlocksFree %#RX64\n", RT_BE2H_U64(pSb->cBlocksFree)));
286 Log2(("XFS: cExtentsRtFree %#RX64\n", RT_BE2H_U64(pSb->cExtentsRtFree)));
287 Log2(("XFS: uInodeQuotaUsr %#RX64\n", RT_BE2H_U64(pSb->uInodeQuotaUsr)));
288 Log2(("XFS: uInodeQuotaGrp %#RX64\n", RT_BE2H_U64(pSb->uInodeQuotaGrp)));
289 Log2(("XFS: fQuotaFlags %#RX16\n", RT_BE2H_U16(pSb->fQuotaFlags)));
290 Log2(("XFS: fFlagsMisc %#RX8\n", pSb->fFlagsMisc));
291 Log2(("XFS: uSharedVn %#RX8\n", pSb->uSharedVn));
292 Log2(("XFS: cBlocksInodeAlignment %#RX32\n", RT_BE2H_U32(pSb->cBlocksInodeAlignment)));
293 Log2(("XFS: cBlocksRaidStripe %#RX32\n", RT_BE2H_U32(pSb->cBlocksRaidStripe)));
294 Log2(("XFS: cBlocksRaidWidth %#RX32\n", RT_BE2H_U32(pSb->cBlocksRaidWidth)));
295 Log2(("XFS: cDirBlockAllocLog %RU8\n", pSb->cDirBlockAllocLog));
296 Log2(("XFS: cLogDevSubVolSectorSzLog %RU8\n", pSb->cLogDevSubVolSectorSzLog));
297 Log2(("XFS: cLogDevSectorSzLog %RU16\n", RT_BE2H_U16(pSb->cLogDevSectorSzLog)));
298 Log2(("XFS: cLogDevRaidStripe %RU32\n", RT_BE2H_U32(pSb->cLogDevRaidStripe)));
299 Log2(("XFS: fFeatures2 %#RX32\n", RT_BE2H_U32(pSb->fFeatures2)));
300 Log2(("XFS: fFeaturesRw %#RX32\n", RT_BE2H_U32(pSb->fFeaturesRw)));
301 Log2(("XFS: fFeaturesRo %#RX32\n", RT_BE2H_U32(pSb->fFeaturesRo)));
302 Log2(("XFS: fFeaturesIncompatRw %#RX32\n", RT_BE2H_U32(pSb->fFeaturesIncompatRw)));
303 Log2(("XFS: fFeaturesJrnlIncompatRw %#RX32\n", RT_BE2H_U32(pSb->fFeaturesJrnlIncompatRw)));
304 Log2(("XFS: u32Chksum %#RX32\n", RT_BE2H_U32(pSb->u32Chksum)));
305 Log2(("XFS: u32SparseInodeAlignment %#RX32\n", RT_BE2H_U32(pSb->u32SparseInodeAlignment)));
306 Log2(("XFS: uInodeProjectQuota %#RX64\n", RT_BE2H_U64(pSb->uInodeProjectQuota)));
307 Log2(("XFS: uJrnlSeqSbUpdate %#RX64\n", RT_BE2H_U64(pSb->uJrnlSeqSbUpdate)));
308 Log2(("XFS: abUuidMeta <todo>\n"));
309 Log2(("XFS: uInodeRm %#RX64\n", RT_BE2H_U64(pSb->uInodeRm)));
310 }
311}
312
313
314#if 0
315/**
316 * Logs a XFS filesystem inode.
317 *
318 * @returns nothing.
319 * @param pThis The XFS volume instance.
320 * @param iInode Inode number.
321 * @param pInode Pointer to the inode.
322 */
323static void rtFsXfsInode_Log(PRTFSXFSVOL pThis, XFSINO iInode, PCXFSINODE pInode)
324{
325 if (LogIs2Enabled())
326 {
327
328 }
329}
330
331
332/**
333 * Logs a XFS filesystem directory entry.
334 *
335 * @returns nothing.
336 * @param pThis The XFS volume instance.
337 * @param idxDirEntry Directory entry index number.
338 * @param pDirEntry The directory entry.
339 */
340static void rtFsXfsDirEntry_Log(PRTFSXFSVOL pThis, uint32_t idxDirEntry, PCXFSDIRENTRYEX pDirEntry)
341{
342 if (LogIs2Enabled())
343 {
344 }
345}
346#endif
347#endif
348
349
350/**
351 * Converts a block number to a byte offset.
352 *
353 * @returns Offset in bytes for the given block number.
354 * @param pThis The XFS volume instance.
355 * @param iBlock The block number to convert.
356 */
357DECLINLINE(uint64_t) rtFsXfsBlockIdxToDiskOffset(PRTFSXFSVOL pThis, uint64_t iBlock)
358{
359 return iBlock << pThis->cBlockShift;
360}
361
362
363/**
364 * Converts a byte offset to a block number.
365 *
366 * @returns Block number.
367 * @param pThis The XFS volume instance.
368 * @param iBlock The offset to convert.
369 */
370DECLINLINE(uint64_t) rtFsXfsDiskOffsetToBlockIdx(PRTFSXFSVOL pThis, uint64_t off)
371{
372 return off >> pThis->cBlockShift;
373}
374
375
376/**
377 * Allocates a new block group.
378 *
379 * @returns Pointer to the new block group descriptor or NULL if out of memory.
380 * @param pThis The XFS volume instance.
381 * @param cbAlloc How much to allocate.
382 * @param iBlockGroup Block group number.
383 */
384static PRTFSXFSBLOCKENTRY rtFsXfsVol_BlockAlloc(PRTFSXFSVOL pThis, size_t cbAlloc, uint64_t iBlock)
385{
386 PRTFSXFSBLOCKENTRY pBlock = (PRTFSXFSBLOCKENTRY)RTMemAllocZ(cbAlloc);
387 if (RT_LIKELY(pBlock))
388 {
389 pBlock->Core.Key = iBlock;
390 pBlock->cRefs = 0;
391 pThis->cbBlocks += cbAlloc;
392 }
393
394 return pBlock;
395}
396
397
398/**
399 * Returns a new block entry utilizing the cache if possible.
400 *
401 * @returns Pointer to the new block entry or NULL if out of memory.
402 * @param pThis The XFS volume instance.
403 * @param iBlock Block number.
404 */
405static PRTFSXFSBLOCKENTRY rtFsXfsVol_BlockGetNew(PRTFSXFSVOL pThis, uint64_t iBlock)
406{
407 PRTFSXFSBLOCKENTRY pBlock = NULL;
408 size_t cbAlloc = RT_UOFFSETOF_DYN(RTFSXFSBLOCKENTRY, abData[pThis->cbBlock]);
409 if (pThis->cbBlocks + cbAlloc <= RTFSXFS_MAX_BLOCK_CACHE_SIZE)
410 pBlock = rtFsXfsVol_BlockAlloc(pThis, cbAlloc, iBlock);
411 else
412 {
413 pBlock = RTListRemoveLast(&pThis->LstBlockLru, RTFSXFSBLOCKENTRY, NdLru);
414 if (!pBlock)
415 pBlock = rtFsXfsVol_BlockAlloc(pThis, cbAlloc, iBlock);
416 else
417 {
418 /* Remove the block group from the tree because it gets a new key. */
419 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
420 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
421 }
422 }
423
424 Assert(!pBlock->cRefs);
425 pBlock->Core.Key = iBlock;
426 pBlock->cRefs = 1;
427
428 return pBlock;
429}
430
431
432/**
433 * Frees the given block.
434 *
435 * @returns nothing.
436 * @param pThis The XFS volume instance.
437 * @param pBlock The block to free.
438 */
439static void rtFsXfsVol_BlockFree(PRTFSXFSVOL pThis, PRTFSXFSBLOCKENTRY pBlock)
440{
441 Assert(!pBlock->cRefs);
442
443 /*
444 * Put it into the cache if the limit wasn't exceeded, otherwise the block group
445 * is freed right away.
446 */
447 if (pThis->cbBlocks <= RTFSXFS_MAX_BLOCK_CACHE_SIZE)
448 {
449 /* Put onto the LRU list. */
450 RTListPrepend(&pThis->LstBlockLru, &pBlock->NdLru);
451 }
452 else
453 {
454 /* Remove from the tree and free memory. */
455 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
456 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
457 RTMemFree(pBlock);
458 pThis->cbBlocks -= RT_UOFFSETOF_DYN(RTFSXFSBLOCKENTRY, abData[pThis->cbBlock]);
459 }
460}
461
462
463/**
464 * Gets the specified block data from the volume.
465 *
466 * @returns IPRT status code.
467 * @param pThis The XFS volume instance.
468 * @param iBlock The filesystem block to load.
469 * @param ppBlock Where to return the pointer to the block entry on success.
470 * @param ppvData Where to return the pointer to the block data on success.
471 */
472static int rtFsXfsVol_BlockLoad(PRTFSXFSVOL pThis, uint64_t iBlock, PRTFSXFSBLOCKENTRY *ppBlock, void **ppvData)
473{
474 int rc = VINF_SUCCESS;
475
476 /* Try to fetch the block group from the cache first. */
477 PRTFSXFSBLOCKENTRY pBlock = (PRTFSXFSBLOCKENTRY)RTAvlU64Get(&pThis->BlockRoot, iBlock);
478 if (!pBlock)
479 {
480 /* Slow path, load from disk. */
481 pBlock = rtFsXfsVol_BlockGetNew(pThis, iBlock);
482 if (RT_LIKELY(pBlock))
483 {
484 uint64_t offRead = rtFsXfsBlockIdxToDiskOffset(pThis, iBlock);
485 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlock->abData[0], pThis->cbBlock, NULL);
486 if (RT_SUCCESS(rc))
487 {
488 bool fIns = RTAvlU64Insert(&pThis->BlockRoot, &pBlock->Core);
489 Assert(fIns); RT_NOREF(fIns);
490 }
491 }
492 else
493 rc = VERR_NO_MEMORY;
494 }
495 else
496 {
497 /* Remove from current LRU list position and add to the beginning. */
498 uint32_t cRefs = ASMAtomicIncU32(&pBlock->cRefs);
499 if (cRefs == 1) /* Blocks get removed from the LRU list if they are referenced. */
500 RTListNodeRemove(&pBlock->NdLru);
501 }
502
503 if (RT_SUCCESS(rc))
504 {
505 *ppBlock = pBlock;
506 *ppvData = &pBlock->abData[0];
507 }
508 else if (pBlock)
509 {
510 ASMAtomicDecU32(&pBlock->cRefs);
511 rtFsXfsVol_BlockFree(pThis, pBlock); /* Free the block. */
512 }
513
514 return rc;
515}
516
517
518/**
519 * Releases a reference of the given block.
520 *
521 * @returns nothing.
522 * @param pThis The XFS volume instance.
523 * @param pBlock The block to release.
524 */
525static void rtFsXfsVol_BlockRelease(PRTFSXFSVOL pThis, PRTFSXFSBLOCKENTRY pBlock)
526{
527 uint32_t cRefs = ASMAtomicDecU32(&pBlock->cRefs);
528 if (!cRefs)
529 rtFsXfsVol_BlockFree(pThis, pBlock);
530}
531
532
533/**
534 * Allocates a new alloction group.
535 *
536 * @returns Pointer to the new allocation group descriptor or NULL if out of memory.
537 * @param pThis The XFS volume instance.
538 * @param iAG Allocation group number.
539 */
540static PRTFSXFSAG rtFsXfsAg_Alloc(PRTFSXFSVOL pThis, uint32_t iAg)
541{
542 PRTFSXFSAG pAg = (PRTFSXFSAG)RTMemAllocZ(sizeof(RTFSXFSAG));
543 if (RT_LIKELY(pAg))
544 {
545 pAg->Core.Key = iAg;
546 pAg->cRefs = 0;
547 pThis->cbAgs += sizeof(RTFSXFSAG);
548 }
549
550 return pAg;
551}
552
553
554/**
555 * Frees the given allocation group.
556 *
557 * @returns nothing.
558 * @param pThis The XFS volume instance.
559 * @param pAg The allocation group to free.
560 */
561static void rtFsXfsAg_Free(PRTFSXFSVOL pThis, PRTFSXFSAG pAg)
562{
563 Assert(!pAg->cRefs);
564
565 /*
566 * Put it into the cache if the limit wasn't exceeded, otherwise the allocation group
567 * is freed right away.
568 */
569 if (pThis->cbAgs <= RTFSXFS_MAX_AG_CACHE_SIZE)
570 {
571 /* Put onto the LRU list. */
572 RTListPrepend(&pThis->LstAgLru, &pAg->NdLru);
573 }
574 else
575 {
576 /* Remove from the tree and free memory. */
577 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->AgRoot, pAg->Core.Key);
578 Assert(pCore == &pAg->Core); RT_NOREF(pCore);
579 RTMemFree(pAg);
580 pThis->cbAgs -= sizeof(RTFSXFSAG);
581 }
582}
583
584
585/**
586 * Returns a new block group utilizing the cache if possible.
587 *
588 * @returns Pointer to the new block group descriptor or NULL if out of memory.
589 * @param pThis The XFS volume instance.
590 * @param iAg Allocation group number.
591 */
592static PRTFSXFSAG rtFsXfsAg_GetNew(PRTFSXFSVOL pThis, uint32_t iAg)
593{
594 PRTFSXFSAG pAg = NULL;
595 if (pThis->cbAgs + sizeof(RTFSXFSAG) <= RTFSXFS_MAX_AG_CACHE_SIZE)
596 pAg = rtFsXfsAg_Alloc(pThis, iAg);
597 else
598 {
599 pAg = RTListRemoveLast(&pThis->LstAgLru, RTFSXFSAG, NdLru);
600 if (!pAg)
601 pAg = rtFsXfsAg_Alloc(pThis, iAg);
602 else
603 {
604 /* Remove the block group from the tree because it gets a new key. */
605 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->AgRoot, pAg->Core.Key);
606 Assert(pCore == &pAg->Core); RT_NOREF(pCore);
607 }
608 }
609
610 Assert(!pAg->cRefs);
611 pAg->Core.Key = iAg;
612 pAg->cRefs = 1;
613
614 return pAg;
615}
616
617
618/**
619 * Loads the given allocation group number and returns it on success.
620 *
621 * @returns IPRT status code.
622 * @param pThis The XFS volume instance.
623 * @param iAg The allocation group to load.
624 * @param ppAg Where to store the allocation group on success.
625 */
626static int rtFsXfsAg_Load(PRTFSXFSVOL pThis, uint32_t iAg, PRTFSXFSAG *ppAg)
627{
628 int rc = VINF_SUCCESS;
629
630 /* Try to fetch the allocation group from the cache first. */
631 PRTFSXFSAG pAg = (PRTFSXFSAG)RTAvlU32Get(&pThis->AgRoot, iAg);
632 if (!pAg)
633 {
634 /* Slow path, load from disk. */
635 pAg = rtFsXfsAg_GetNew(pThis, iAg);
636 if (RT_LIKELY(pAg))
637 {
638#if 0 /** @todo */
639 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pThis->cbBlock == _1K ? 2 : 1)
640 + (uint64_t)iBlockGroup * pThis->cbBlkGrpDesc;
641 EXTBLOCKGROUPDESC BlockGroupDesc;
642 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &BlockGroupDesc, pThis->cbBlkGrpDesc, NULL);
643 if (RT_SUCCESS(rc))
644 {
645#ifdef LOG_ENABLED
646 rtFsExtBlockGroup_Log(pThis, iBlockGroup, &BlockGroupDesc);
647#endif
648 pBlockGroup->iBlockInodeTbl = RT_LE2H_U32(BlockGroupDesc.v32.offInodeTableLow)
649 | ((pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
650 ? (uint64_t)RT_LE2H_U32(BlockGroupDesc.v64.offInodeTableHigh) << 32
651 : 0);
652
653 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offBlockBitmapLow),
654 RT_LE2H_U32(BlockGroupDesc.v64.offBlockBitmapHigh));
655 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->abBlockBitmap[0], pThis->cbBlockBitmap, NULL);
656 if (RT_SUCCESS(rc))
657 {
658 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offInodeBitmapLow),
659 RT_LE2H_U32(BlockGroupDesc.v64.offInodeBitmapHigh));
660 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->pabInodeBitmap[0], pThis->cbInodeBitmap, NULL);
661 if (RT_SUCCESS(rc))
662 {
663 bool fIns = RTAvlU32Insert(&pThis->BlockGroupRoot, &pBlockGroup->Core);
664 Assert(fIns); RT_NOREF(fIns);
665 }
666 }
667 }
668#endif
669 }
670 else
671 rc = VERR_NO_MEMORY;
672 }
673 else
674 {
675 /* Remove from current LRU list position and add to the beginning. */
676 uint32_t cRefs = ASMAtomicIncU32(&pAg->cRefs);
677 if (cRefs == 1) /* Block groups get removed from the LRU list if they are referenced. */
678 RTListNodeRemove(&pAg->NdLru);
679 }
680
681 if (RT_SUCCESS(rc))
682 *ppAg = pAg;
683 else if (pAg)
684 {
685 ASMAtomicDecU32(&pAg->cRefs);
686 rtFsXfsAg_Free(pThis, pAg); /* Free the allocation group. */
687 }
688
689 return rc;
690}
691
692
693/**
694 * Releases a reference of the given allocation group.
695 *
696 * @returns nothing.
697 * @param pThis The XFS volume instance.
698 * @param pAg The allocation group to release.
699 */
700static void rtFsXfsAg_Release(PRTFSXFSVOL pThis, PRTFSXFSAG pAg)
701{
702 uint32_t cRefs = ASMAtomicDecU32(&pAg->cRefs);
703 if (!cRefs)
704 rtFsXfsAg_Free(pThis, pAg);
705}
706
707
708/**
709 * Allocates a new inode.
710 *
711 * @returns Pointer to the new inode or NULL if out of memory.
712 * @param pThis The XFS volume instance.
713 * @param iInode Inode number.
714 */
715static PRTFSXFSINODE rtFsXfsInode_Alloc(PRTFSXFSVOL pThis, uint32_t iInode)
716{
717 PRTFSXFSINODE pInode = (PRTFSXFSINODE)RTMemAllocZ(sizeof(RTFSXFSINODE));
718 if (RT_LIKELY(pInode))
719 {
720 pInode->Core.Key = iInode;
721 pInode->cRefs = 0;
722 pThis->cbInodes += sizeof(RTFSXFSINODE);
723 }
724
725 return pInode;
726}
727
728
729/**
730 * Frees the given inode.
731 *
732 * @returns nothing.
733 * @param pThis The XFS volume instance.
734 * @param pInode The inode to free.
735 */
736static void rtFsXfsInode_Free(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode)
737{
738 Assert(!pInode->cRefs);
739
740 /*
741 * Put it into the cache if the limit wasn't exceeded, otherwise the inode
742 * is freed right away.
743 */
744 if (pThis->cbInodes <= RTFSXFS_MAX_INODE_CACHE_SIZE)
745 {
746 /* Put onto the LRU list. */
747 RTListPrepend(&pThis->LstInodeLru, &pInode->NdLru);
748 }
749 else
750 {
751 /* Remove from the tree and free memory. */
752 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
753 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
754 RTMemFree(pInode);
755 pThis->cbInodes -= sizeof(RTFSXFSINODE);
756 }
757}
758
759
760/**
761 * Returns a new inodep utilizing the cache if possible.
762 *
763 * @returns Pointer to the new inode or NULL if out of memory.
764 * @param pThis The XFS volume instance.
765 * @param iInode Inode number.
766 */
767static PRTFSXFSINODE rtFsXfsInode_GetNew(PRTFSXFSVOL pThis, uint32_t iInode)
768{
769 PRTFSXFSINODE pInode = NULL;
770 if (pThis->cbInodes + sizeof(RTFSXFSINODE) <= RTFSXFS_MAX_INODE_CACHE_SIZE)
771 pInode = rtFsXfsInode_Alloc(pThis, iInode);
772 else
773 {
774 pInode = RTListRemoveLast(&pThis->LstInodeLru, RTFSXFSINODE, NdLru);
775 if (!pInode)
776 pInode = rtFsXfsInode_Alloc(pThis, iInode);
777 else
778 {
779 /* Remove the block group from the tree because it gets a new key. */
780 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
781 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
782 }
783 }
784
785 Assert(!pInode->cRefs);
786 pInode->Core.Key = iInode;
787 pInode->cRefs = 1;
788
789 return pInode;
790}
791
792
793/**
794 * Loads the given inode number and returns it on success.
795 *
796 * @returns IPRT status code.
797 * @param pThis The XFS volume instance.
798 * @param iInode The inode to load.
799 * @param ppInode Where to store the inode on success.
800 */
801static int rtFsXfsInode_Load(PRTFSXFSVOL pThis, uint32_t iInode, PRTFSXFSINODE *ppInode)
802{
803 int rc = VINF_SUCCESS;
804
805 /* Try to fetch the inode from the cache first. */
806 PRTFSXFSINODE pInode = (PRTFSXFSINODE)RTAvlU32Get(&pThis->InodeRoot, iInode);
807 if (!pInode)
808 {
809 /* Slow path, load from disk. */
810 pInode = rtFsXfsInode_GetNew(pThis, iInode);
811 if (RT_LIKELY(pInode))
812 {
813#if 0 /** @todo */
814 /* Calculate the block group and load that one first to get at the inode table location. */
815 PRTFSEXTBLKGRP pBlockGroup = NULL;
816 rc = rtFsEBlockGroupLoad(pThis, (iInode - 1) / pThis->cInodesPerGroup, &pBlockGroup);
817 if (RT_SUCCESS(rc))
818 {
819 uint32_t idxInodeInTbl = (iInode - 1) % pThis->cInodesPerGroup;
820 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pBlockGroup->iBlockInodeTbl)
821 + idxInodeInTbl * pThis->cbInode;
822
823 /* Release block group here already as it is not required. */
824 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
825
826 EXTINODECOMB Inode;
827 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &Inode, RT_MIN(sizeof(Inode), pThis->cbInode), NULL);
828 if (RT_SUCCESS(rc))
829 {
830#ifdef LOG_ENABLED
831 rtFsExtInode_Log(pThis, iInode, &Inode);
832#endif
833 pInode->offInode = offRead;
834 pInode->fFlags = RT_LE2H_U32(Inode.Core.fFlags);
835 pInode->ObjInfo.cbObject = (uint64_t)RT_LE2H_U32(Inode.Core.cbSizeHigh) << 32
836 | (uint64_t)RT_LE2H_U32(Inode.Core.cbSizeLow);
837 pInode->ObjInfo.cbAllocated = ( (uint64_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.cBlocksHigh) << 32
838 | (uint64_t)RT_LE2H_U32(Inode.Core.cBlocksLow)) * pThis->cbBlock;
839 RTTimeSpecSetSeconds(&pInode->ObjInfo.AccessTime, RT_LE2H_U32(Inode.Core.u32TimeLastAccess));
840 RTTimeSpecSetSeconds(&pInode->ObjInfo.ModificationTime, RT_LE2H_U32(Inode.Core.u32TimeLastModification));
841 RTTimeSpecSetSeconds(&pInode->ObjInfo.ChangeTime, RT_LE2H_U32(Inode.Core.u32TimeLastChange));
842 pInode->ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
843 pInode->ObjInfo.Attr.u.Unix.uid = (uint32_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.uUidHigh) << 16
844 | (uint32_t)RT_LE2H_U16(Inode.Core.uUidLow);
845 pInode->ObjInfo.Attr.u.Unix.gid = (uint32_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.uGidHigh) << 16
846 | (uint32_t)RT_LE2H_U16(Inode.Core.uGidLow);
847 pInode->ObjInfo.Attr.u.Unix.cHardlinks = RT_LE2H_U16(Inode.Core.cHardLinks);
848 pInode->ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
849 pInode->ObjInfo.Attr.u.Unix.INodeId = iInode;
850 pInode->ObjInfo.Attr.u.Unix.fFlags = 0;
851 pInode->ObjInfo.Attr.u.Unix.GenerationId = RT_LE2H_U32(Inode.Core.u32Version);
852 pInode->ObjInfo.Attr.u.Unix.Device = 0;
853 if (pThis->cbInode >= sizeof(EXTINODECOMB))
854 RTTimeSpecSetSeconds(&pInode->ObjInfo.BirthTime, RT_LE2H_U32(Inode.Extra.u32TimeCreation));
855 else
856 RTTimeSpecSetSeconds(&pInode->ObjInfo.BirthTime, RT_LE2H_U32(Inode.Core.u32TimeLastChange));
857 for (unsigned i = 0; i < RT_ELEMENTS(pInode->aiBlocks); i++)
858 pInode->aiBlocks[i] = RT_LE2H_U32(Inode.Core.au32Block[i]);
859
860 /* Fill in the mode. */
861 pInode->ObjInfo.Attr.fMode = 0;
862 uint32_t fInodeMode = RT_LE2H_U32(Inode.Core.fMode);
863 switch (EXT_INODE_MODE_TYPE_GET_TYPE(fInodeMode))
864 {
865 case EXT_INODE_MODE_TYPE_FIFO:
866 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FIFO;
867 break;
868 case EXT_INODE_MODE_TYPE_CHAR:
869 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_CHAR;
870 break;
871 case EXT_INODE_MODE_TYPE_DIR:
872 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DIRECTORY;
873 break;
874 case EXT_INODE_MODE_TYPE_BLOCK:
875 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_BLOCK;
876 break;
877 case EXT_INODE_MODE_TYPE_REGULAR:
878 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FILE;
879 break;
880 case EXT_INODE_MODE_TYPE_SYMLINK:
881 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SYMLINK;
882 break;
883 case EXT_INODE_MODE_TYPE_SOCKET:
884 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SOCKET;
885 break;
886 default:
887 rc = VERR_VFS_BOGUS_FORMAT;
888 }
889 if (fInodeMode & EXT_INODE_MODE_EXEC_OTHER)
890 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXOTH;
891 if (fInodeMode & EXT_INODE_MODE_WRITE_OTHER)
892 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWOTH;
893 if (fInodeMode & EXT_INODE_MODE_READ_OTHER)
894 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IROTH;
895 if (fInodeMode & EXT_INODE_MODE_EXEC_GROUP)
896 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXGRP;
897 if (fInodeMode & EXT_INODE_MODE_WRITE_GROUP)
898 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWGRP;
899 if (fInodeMode & EXT_INODE_MODE_READ_GROUP)
900 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRGRP;
901 if (fInodeMode & EXT_INODE_MODE_EXEC_OWNER)
902 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXUSR;
903 if (fInodeMode & EXT_INODE_MODE_WRITE_OWNER)
904 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWUSR;
905 if (fInodeMode & EXT_INODE_MODE_READ_OWNER)
906 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRUSR;
907 if (fInodeMode & EXT_INODE_MODE_STICKY)
908 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISTXT;
909 if (fInodeMode & EXT_INODE_MODE_SET_GROUP_ID)
910 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISGID;
911 if (fInodeMode & EXT_INODE_MODE_SET_USER_ID)
912 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISUID;
913 }
914 }
915#endif
916 }
917 else
918 rc = VERR_NO_MEMORY;
919 }
920 else
921 {
922 /* Remove from current LRU list position and add to the beginning. */
923 uint32_t cRefs = ASMAtomicIncU32(&pInode->cRefs);
924 if (cRefs == 1) /* Inodes get removed from the LRU list if they are referenced. */
925 RTListNodeRemove(&pInode->NdLru);
926 }
927
928 if (RT_SUCCESS(rc))
929 *ppInode = pInode;
930 else if (pInode)
931 {
932 ASMAtomicDecU32(&pInode->cRefs);
933 rtFsXfsInode_Free(pThis, pInode); /* Free the inode. */
934 }
935
936 return rc;
937}
938
939
940/**
941 * Releases a reference of the given inode.
942 *
943 * @returns nothing.
944 * @param pThis The XFS volume instance.
945 * @param pInode The inode to release.
946 */
947static void rtFsXfsInode_Release(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode)
948{
949 uint32_t cRefs = ASMAtomicDecU32(&pInode->cRefs);
950 if (!cRefs)
951 rtFsXfsInode_Free(pThis, pInode);
952}
953
954
955/**
956 * Worker for various QueryInfo methods.
957 *
958 * @returns IPRT status code.
959 * @param pInode The inode structure to return info for.
960 * @param pObjInfo Where to return object info.
961 * @param enmAddAttr What additional info to return.
962 */
963static int rtFsXfsInode_QueryInfo(PRTFSXFSINODE pInode, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
964{
965 RT_ZERO(*pObjInfo);
966
967 pObjInfo->cbObject = pInode->ObjInfo.cbObject;
968 pObjInfo->cbAllocated = pInode->ObjInfo.cbAllocated;
969 pObjInfo->AccessTime = pInode->ObjInfo.AccessTime;
970 pObjInfo->ModificationTime = pInode->ObjInfo.ModificationTime;
971 pObjInfo->ChangeTime = pInode->ObjInfo.ChangeTime;
972 pObjInfo->BirthTime = pInode->ObjInfo.BirthTime;
973 pObjInfo->Attr.fMode = pInode->ObjInfo.Attr.fMode;
974 pObjInfo->Attr.enmAdditional = enmAddAttr;
975 switch (enmAddAttr)
976 {
977 case RTFSOBJATTRADD_UNIX:
978 memcpy(&pObjInfo->Attr.u.Unix, &pInode->ObjInfo.Attr.u.Unix, sizeof(pInode->ObjInfo.Attr.u.Unix));
979 break;
980
981 case RTFSOBJATTRADD_UNIX_OWNER:
982 pObjInfo->Attr.u.UnixOwner.uid = pInode->ObjInfo.Attr.u.Unix.uid;
983 break;
984
985 case RTFSOBJATTRADD_UNIX_GROUP:
986 pObjInfo->Attr.u.UnixGroup.gid = pInode->ObjInfo.Attr.u.Unix.gid;
987 break;
988
989 default:
990 break;
991 }
992
993 return VINF_SUCCESS;
994}
995
996
997/**
998 * Maps the given inode block to the destination filesystem block.
999 *
1000 * @returns IPRT status code.
1001 * @param pThis The XFS volume instance.
1002 * @param pInode The inode structure to read from.
1003 * @param iBlock The inode block to map.
1004 * @param cBlocks Number of blocks requested.
1005 * @param piBlockFs Where to store the filesystem block on success.
1006 * @param pcBlocks Where to store the number of contiguous blocks on success.
1007 * @param pfSparse Where to store the sparse flag on success.
1008 *
1009 * @todo Optimize
1010 */
1011static int rtFsXfsInode_MapBlockToFs(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode, uint64_t iBlock, size_t cBlocks,
1012 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1013{
1014 RT_NOREF(pThis, pInode, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse);
1015 return VERR_NOT_IMPLEMENTED;
1016}
1017
1018
1019/**
1020 * Reads data from the given inode at the given byte offset.
1021 *
1022 * @returns IPRT status code.
1023 * @param pThis The XFS volume instance.
1024 * @param pInode The inode structure to read from.
1025 * @param off The byte offset to start reading from.
1026 * @param pvBuf Where to store the read data to.
1027 * @param pcbRead Where to return the amount of data read.
1028 */
1029static int rtFsXfsInode_Read(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode, uint64_t off, void *pvBuf, size_t cbRead, size_t *pcbRead)
1030{
1031 int rc = VINF_SUCCESS;
1032 uint8_t *pbBuf = (uint8_t *)pvBuf;
1033
1034 if (((uint64_t)pInode->ObjInfo.cbObject < off + cbRead))
1035 {
1036 if (!pcbRead)
1037 return VERR_EOF;
1038 else
1039 cbRead = (uint64_t)pInode->ObjInfo.cbObject - off;
1040 }
1041
1042 while ( cbRead
1043 && RT_SUCCESS(rc))
1044 {
1045 uint64_t iBlockStart = rtFsXfsDiskOffsetToBlockIdx(pThis, off);
1046 uint32_t offBlockStart = off % pThis->cbBlock;
1047
1048 /* Resolve the inode block to the proper filesystem block. */
1049 uint64_t iBlockFs = 0;
1050 size_t cBlocks = 0;
1051 bool fSparse = false;
1052 rc = rtFsXfsInode_MapBlockToFs(pThis, pInode, iBlockStart, 1, &iBlockFs, &cBlocks, &fSparse);
1053 if (RT_SUCCESS(rc))
1054 {
1055 Assert(cBlocks == 1);
1056
1057 size_t cbThisRead = RT_MIN(cbRead, pThis->cbBlock - offBlockStart);
1058
1059 if (!fSparse)
1060 {
1061 uint64_t offRead = rtFsXfsBlockIdxToDiskOffset(pThis, iBlockFs);
1062 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead + offBlockStart, pbBuf, cbThisRead, NULL);
1063 }
1064 else
1065 memset(pbBuf, 0, cbThisRead);
1066
1067 if (RT_SUCCESS(rc))
1068 {
1069 pbBuf += cbThisRead;
1070 cbRead -= cbThisRead;
1071 off += cbThisRead;
1072 if (pcbRead)
1073 *pcbRead += cbThisRead;
1074 }
1075 }
1076 }
1077
1078 return rc;
1079}
1080
1081
1082
1083/*
1084 *
1085 * File operations.
1086 * File operations.
1087 * File operations.
1088 *
1089 */
1090
1091/**
1092 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1093 */
1094static DECLCALLBACK(int) rtFsXfsFile_Close(void *pvThis)
1095{
1096 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1097 LogFlow(("rtFsXfsFile_Close(%p/%p)\n", pThis, pThis->pInode));
1098
1099 rtFsXfsInode_Release(pThis->pVol, pThis->pInode);
1100 pThis->pInode = NULL;
1101 pThis->pVol = NULL;
1102 return VINF_SUCCESS;
1103}
1104
1105
1106/**
1107 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1108 */
1109static DECLCALLBACK(int) rtFsXfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1110{
1111 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1112 return rtFsXfsInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
1113}
1114
1115
1116/**
1117 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1118 */
1119static DECLCALLBACK(int) rtFsXfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1120{
1121 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1122 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
1123 RT_NOREF(fBlocking);
1124
1125 if (off == -1)
1126 off = pThis->offFile;
1127 else
1128 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
1129
1130 int rc;
1131 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
1132 if (!pcbRead)
1133 {
1134 rc = rtFsXfsInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1135 if (RT_SUCCESS(rc))
1136 pThis->offFile = off + cbRead;
1137 Log6(("rtFsExtFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
1138 }
1139 else
1140 {
1141 PRTFSXFSINODE pInode = pThis->pInode;
1142 if (off >= pInode->ObjInfo.cbObject)
1143 {
1144 *pcbRead = 0;
1145 rc = VINF_EOF;
1146 }
1147 else
1148 {
1149 if ((uint64_t)off + cbRead <= (uint64_t)pInode->ObjInfo.cbObject)
1150 rc = rtFsXfsInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1151 else
1152 {
1153 /* Return VINF_EOF if beyond end-of-file. */
1154 cbRead = (size_t)(pInode->ObjInfo.cbObject - off);
1155 rc = rtFsXfsInode_Read(pThis->pVol, pThis->pInode, off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1156 if (RT_SUCCESS(rc))
1157 rc = VINF_EOF;
1158 }
1159 if (RT_SUCCESS(rc))
1160 {
1161 pThis->offFile = off + cbRead;
1162 *pcbRead = cbRead;
1163 }
1164 else
1165 *pcbRead = 0;
1166 }
1167 Log6(("rtFsXfsFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
1168 }
1169
1170 return rc;
1171}
1172
1173
1174/**
1175 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1176 */
1177static DECLCALLBACK(int) rtFsXfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1178{
1179 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
1180 return VERR_WRITE_PROTECT;
1181}
1182
1183
1184/**
1185 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1186 */
1187static DECLCALLBACK(int) rtFsXfsFile_Flush(void *pvThis)
1188{
1189 RT_NOREF(pvThis);
1190 return VINF_SUCCESS;
1191}
1192
1193
1194/**
1195 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1196 */
1197static DECLCALLBACK(int) rtFsXfsFile_Tell(void *pvThis, PRTFOFF poffActual)
1198{
1199 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1200 *poffActual = pThis->offFile;
1201 return VINF_SUCCESS;
1202}
1203
1204
1205/**
1206 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1207 */
1208static DECLCALLBACK(int) rtFsXfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1209{
1210 RT_NOREF(pvThis, fMode, fMask);
1211 return VERR_WRITE_PROTECT;
1212}
1213
1214
1215/**
1216 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1217 */
1218static DECLCALLBACK(int) rtFsXfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1219 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1220{
1221 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1222 return VERR_WRITE_PROTECT;
1223}
1224
1225
1226/**
1227 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1228 */
1229static DECLCALLBACK(int) rtFsXfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1230{
1231 RT_NOREF(pvThis, uid, gid);
1232 return VERR_WRITE_PROTECT;
1233}
1234
1235
1236/**
1237 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1238 */
1239static DECLCALLBACK(int) rtFsXfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1240{
1241 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1242 RTFOFF offNew;
1243 switch (uMethod)
1244 {
1245 case RTFILE_SEEK_BEGIN:
1246 offNew = offSeek;
1247 break;
1248 case RTFILE_SEEK_END:
1249 offNew = pThis->pInode->ObjInfo.cbObject + offSeek;
1250 break;
1251 case RTFILE_SEEK_CURRENT:
1252 offNew = (RTFOFF)pThis->offFile + offSeek;
1253 break;
1254 default:
1255 return VERR_INVALID_PARAMETER;
1256 }
1257 if (offNew >= 0)
1258 {
1259 pThis->offFile = offNew;
1260 *poffActual = offNew;
1261 return VINF_SUCCESS;
1262 }
1263 return VERR_NEGATIVE_SEEK;
1264}
1265
1266
1267/**
1268 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1269 */
1270static DECLCALLBACK(int) rtFsXfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1271{
1272 PRTFSXFSFILE pThis = (PRTFSXFSFILE)pvThis;
1273 *pcbFile = (uint64_t)pThis->pInode->ObjInfo.cbObject;
1274 return VINF_SUCCESS;
1275}
1276
1277
1278/**
1279 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
1280 */
1281static DECLCALLBACK(int) rtFsXfsFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
1282{
1283 RT_NOREF(pvThis, cbFile, fFlags);
1284 return VERR_WRITE_PROTECT;
1285}
1286
1287
1288/**
1289 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
1290 */
1291static DECLCALLBACK(int) rtFsXfsFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
1292{
1293 RT_NOREF(pvThis);
1294 *pcbMax = INT64_MAX; /** @todo */
1295 return VINF_SUCCESS;
1296}
1297
1298
1299/**
1300 * XFS file operations.
1301 */
1302static const RTVFSFILEOPS g_rtFsXfsFileOps =
1303{
1304 { /* Stream */
1305 { /* Obj */
1306 RTVFSOBJOPS_VERSION,
1307 RTVFSOBJTYPE_FILE,
1308 "XFS File",
1309 rtFsXfsFile_Close,
1310 rtFsXfsFile_QueryInfo,
1311 RTVFSOBJOPS_VERSION
1312 },
1313 RTVFSIOSTREAMOPS_VERSION,
1314 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1315 rtFsXfsFile_Read,
1316 rtFsXfsFile_Write,
1317 rtFsXfsFile_Flush,
1318 NULL /*PollOne*/,
1319 rtFsXfsFile_Tell,
1320 NULL /*pfnSkip*/,
1321 NULL /*pfnZeroFill*/,
1322 RTVFSIOSTREAMOPS_VERSION,
1323 },
1324 RTVFSFILEOPS_VERSION,
1325 0,
1326 { /* ObjSet */
1327 RTVFSOBJSETOPS_VERSION,
1328 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1329 rtFsXfsFile_SetMode,
1330 rtFsXfsFile_SetTimes,
1331 rtFsXfsFile_SetOwner,
1332 RTVFSOBJSETOPS_VERSION
1333 },
1334 rtFsXfsFile_Seek,
1335 rtFsXfsFile_QuerySize,
1336 rtFsXfsFile_SetSize,
1337 rtFsXfsFile_QueryMaxSize,
1338 RTVFSFILEOPS_VERSION
1339};
1340
1341
1342/**
1343 * Creates a new VFS file from the given regular file inode.
1344 *
1345 * @returns IPRT status code.
1346 * @param pThis The XFS volume instance.
1347 * @param fOpen Open flags passed.
1348 * @param iInode The inode for the file.
1349 * @param phVfsFile Where to store the VFS file handle on success.
1350 * @param pErrInfo Where to record additional error information on error, optional.
1351 * @param pszWhat Logging prefix.
1352 */
1353static int rtFsXfsVol_NewFile(PRTFSXFSVOL pThis, uint64_t fOpen, uint32_t iInode,
1354 PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo, const char *pszWhat)
1355{
1356 /*
1357 * Load the inode and check that it really is a file.
1358 */
1359 PRTFSXFSINODE pInode = NULL;
1360 int rc = rtFsXfsInode_Load(pThis, iInode, &pInode);
1361 if (RT_SUCCESS(rc))
1362 {
1363 if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
1364 {
1365 PRTFSXFSFILE pNewFile;
1366 rc = RTVfsNewFile(&g_rtFsXfsFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
1367 phVfsFile, (void **)&pNewFile);
1368 if (RT_SUCCESS(rc))
1369 {
1370 pNewFile->pVol = pThis;
1371 pNewFile->pInode = pInode;
1372 pNewFile->offFile = 0;
1373 }
1374 }
1375 else
1376 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: fMode=%#RX32", pszWhat, pInode->ObjInfo.Attr.fMode);
1377
1378 if (RT_FAILURE(rc))
1379 rtFsXfsInode_Release(pThis, pInode);
1380 }
1381
1382 return rc;
1383}
1384
1385
1386
1387/*
1388 *
1389 * XFS directory code.
1390 * XFS directory code.
1391 * XFS directory code.
1392 *
1393 */
1394
1395/**
1396 * Looks up an entry in the given directory inode.
1397 *
1398 * @returns IPRT status code.
1399 * @param pThis The XFS volume instance.
1400 * @param pInode The directory inode structure to.
1401 * @param pszEntry The entry to lookup.
1402 * @param piInode Where to store the inode number if the entry was found.
1403 */
1404static int rtFsXfsDir_Lookup(PRTFSXFSVOL pThis, PRTFSXFSINODE pInode, const char *pszEntry, uint32_t *piInode)
1405{
1406 uint64_t offEntry = 0;
1407 int rc = VERR_FILE_NOT_FOUND;
1408 uint32_t idxDirEntry = 0;
1409 size_t cchEntry = strlen(pszEntry);
1410
1411 if (cchEntry > 255)
1412 return VERR_FILENAME_TOO_LONG;
1413
1414 RT_NOREF(pThis, idxDirEntry, offEntry, pInode, piInode);
1415
1416#if 0 /** @todo */
1417 while (offEntry < (uint64_t)pInode->ObjInfo.cbObject)
1418 {
1419 EXTDIRENTRYEX DirEntry;
1420 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - offEntry);
1421 int rc2 = rtFsXfsInode_Read(pThis, pInode, offEntry, &DirEntry, cbThis, NULL);
1422 if (RT_SUCCESS(rc2))
1423 {
1424#ifdef LOG_ENABLED
1425 rtFsExtDirEntry_Log(pThis, idxDirEntry, &DirEntry);
1426#endif
1427
1428 uint16_t cbName = pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
1429 ? DirEntry.Core.u.v2.cbName
1430 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
1431 if ( cchEntry == cbName
1432 && !memcmp(pszEntry, &DirEntry.Core.achName[0], cchEntry))
1433 {
1434 *piInode = RT_LE2H_U32(DirEntry.Core.iInodeRef);
1435 rc = VINF_SUCCESS;
1436 break;
1437 }
1438
1439 offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
1440 idxDirEntry++;
1441 }
1442 else
1443 {
1444 rc = rc2;
1445 break;
1446 }
1447 }
1448#endif
1449 return rc;
1450}
1451
1452
1453
1454/*
1455 *
1456 * Directory instance methods
1457 * Directory instance methods
1458 * Directory instance methods
1459 *
1460 */
1461
1462/**
1463 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1464 */
1465static DECLCALLBACK(int) rtFsXfsDir_Close(void *pvThis)
1466{
1467 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1468 LogFlowFunc(("pThis=%p\n", pThis));
1469 rtFsXfsInode_Release(pThis->pVol, pThis->pInode);
1470 pThis->pInode = NULL;
1471 return VINF_SUCCESS;
1472}
1473
1474
1475/**
1476 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1477 */
1478static DECLCALLBACK(int) rtFsXfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1479{
1480 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1481 LogFlowFunc(("\n"));
1482 return rtFsXfsInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
1483}
1484
1485
1486/**
1487 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1488 */
1489static DECLCALLBACK(int) rtFsXfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1490{
1491 LogFlowFunc(("\n"));
1492 RT_NOREF(pvThis, fMode, fMask);
1493 return VERR_WRITE_PROTECT;
1494}
1495
1496
1497/**
1498 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1499 */
1500static DECLCALLBACK(int) rtFsXfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1501 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1502{
1503 LogFlowFunc(("\n"));
1504 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1505 return VERR_WRITE_PROTECT;
1506}
1507
1508
1509/**
1510 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1511 */
1512static DECLCALLBACK(int) rtFsXfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1513{
1514 LogFlowFunc(("\n"));
1515 RT_NOREF(pvThis, uid, gid);
1516 return VERR_WRITE_PROTECT;
1517}
1518
1519
1520/**
1521 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
1522 */
1523static DECLCALLBACK(int) rtFsXfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
1524 uint32_t fFlags, PRTVFSOBJ phVfsObj)
1525{
1526 LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
1527 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1528 PRTFSXFSVOL pVol = pThis->pVol;
1529 int rc = VINF_SUCCESS;
1530
1531 RT_NOREF(pThis, pVol, phVfsObj, pszEntry, fFlags);
1532
1533 /*
1534 * We cannot create or replace anything, just open stuff.
1535 */
1536 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
1537 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
1538 { /* likely */ }
1539 else
1540 return VERR_WRITE_PROTECT;
1541
1542 /*
1543 * Lookup the entry.
1544 */
1545 uint32_t iInode = 0;
1546 rc = rtFsXfsDir_Lookup(pVol, pThis->pInode, pszEntry, &iInode);
1547 if (RT_SUCCESS(rc))
1548 {
1549 PRTFSXFSINODE pInode = NULL;
1550 rc = rtFsXfsInode_Load(pVol, iInode, &pInode);
1551 if (RT_SUCCESS(rc))
1552 {
1553 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
1554 {
1555 RTVFSDIR hVfsDir;
1556 rc = rtFsXfsVol_OpenDirByInode(pVol, iInode, &hVfsDir);
1557 if (RT_SUCCESS(rc))
1558 {
1559 *phVfsObj = RTVfsObjFromDir(hVfsDir);
1560 RTVfsDirRelease(hVfsDir);
1561 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
1562 }
1563 }
1564 else if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
1565 {
1566 RTVFSFILE hVfsFile;
1567 rc = rtFsXfsVol_NewFile(pVol, fOpen, iInode, &hVfsFile, NULL, pszEntry);
1568 if (RT_SUCCESS(rc))
1569 {
1570 *phVfsObj = RTVfsObjFromFile(hVfsFile);
1571 RTVfsFileRelease(hVfsFile);
1572 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
1573 }
1574 }
1575 else
1576 rc = VERR_NOT_SUPPORTED;
1577 }
1578 }
1579
1580 LogFlow(("rtFsXfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
1581 return rc;
1582}
1583
1584
1585/**
1586 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
1587 */
1588static DECLCALLBACK(int) rtFsXfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
1589{
1590 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
1591 LogFlowFunc(("\n"));
1592 return VERR_WRITE_PROTECT;
1593}
1594
1595
1596/**
1597 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
1598 */
1599static DECLCALLBACK(int) rtFsXfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
1600{
1601 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
1602 LogFlowFunc(("\n"));
1603 return VERR_NOT_SUPPORTED;
1604}
1605
1606
1607/**
1608 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
1609 */
1610static DECLCALLBACK(int) rtFsXfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
1611 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
1612{
1613 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
1614 LogFlowFunc(("\n"));
1615 return VERR_WRITE_PROTECT;
1616}
1617
1618
1619/**
1620 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
1621 */
1622static DECLCALLBACK(int) rtFsXfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
1623{
1624 RT_NOREF(pvThis, pszEntry, fType);
1625 LogFlowFunc(("\n"));
1626 return VERR_WRITE_PROTECT;
1627}
1628
1629
1630/**
1631 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
1632 */
1633static DECLCALLBACK(int) rtFsXfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
1634{
1635 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
1636 LogFlowFunc(("\n"));
1637 return VERR_WRITE_PROTECT;
1638}
1639
1640
1641/**
1642 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
1643 */
1644static DECLCALLBACK(int) rtFsXfsDir_RewindDir(void *pvThis)
1645{
1646 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1647 LogFlowFunc(("\n"));
1648
1649 pThis->fNoMoreFiles = false;
1650 pThis->offEntry = 0;
1651 pThis->idxEntry = 0;
1652 return VINF_SUCCESS;
1653}
1654
1655
1656/**
1657 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
1658 */
1659static DECLCALLBACK(int) rtFsXfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
1660 RTFSOBJATTRADD enmAddAttr)
1661{
1662 PRTFSXFSDIR pThis = (PRTFSXFSDIR)pvThis;
1663 PRTFSXFSINODE pInode = pThis->pInode;
1664 LogFlowFunc(("\n"));
1665
1666 if (pThis->fNoMoreFiles)
1667 return VERR_NO_MORE_FILES;
1668
1669 RT_NOREF(pInode, pDirEntry, pcbDirEntry, enmAddAttr);
1670 return VERR_NOT_IMPLEMENTED;
1671}
1672
1673
1674/**
1675 * XFS directory operations.
1676 */
1677static const RTVFSDIROPS g_rtFsXfsDirOps =
1678{
1679 { /* Obj */
1680 RTVFSOBJOPS_VERSION,
1681 RTVFSOBJTYPE_DIR,
1682 "XFS Dir",
1683 rtFsXfsDir_Close,
1684 rtFsXfsDir_QueryInfo,
1685 RTVFSOBJOPS_VERSION
1686 },
1687 RTVFSDIROPS_VERSION,
1688 0,
1689 { /* ObjSet */
1690 RTVFSOBJSETOPS_VERSION,
1691 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
1692 rtFsXfsDir_SetMode,
1693 rtFsXfsDir_SetTimes,
1694 rtFsXfsDir_SetOwner,
1695 RTVFSOBJSETOPS_VERSION
1696 },
1697 rtFsXfsDir_Open,
1698 NULL /* pfnFollowAbsoluteSymlink */,
1699 NULL /* pfnOpenFile */,
1700 NULL /* pfnOpenDir */,
1701 rtFsXfsDir_CreateDir,
1702 rtFsXfsDir_OpenSymlink,
1703 rtFsXfsDir_CreateSymlink,
1704 NULL /* pfnQueryEntryInfo */,
1705 rtFsXfsDir_UnlinkEntry,
1706 rtFsXfsDir_RenameEntry,
1707 rtFsXfsDir_RewindDir,
1708 rtFsXfsDir_ReadDir,
1709 RTVFSDIROPS_VERSION,
1710};
1711
1712
1713/**
1714 * Opens a directory by the given inode.
1715 *
1716 * @returns IPRT status code.
1717 * @param pThis The XFS volume instance.
1718 * @param iInode The inode to open.
1719 * @param phVfsDir Where to store the handle to the VFS directory on success.
1720 */
1721static int rtFsXfsVol_OpenDirByInode(PRTFSXFSVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir)
1722{
1723 PRTFSXFSINODE pInode = NULL;
1724 int rc = rtFsXfsInode_Load(pThis, iInode, &pInode);
1725 if (RT_SUCCESS(rc))
1726 {
1727 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
1728 {
1729 PRTFSXFSDIR pNewDir;
1730 rc = RTVfsNewDir(&g_rtFsXfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
1731 phVfsDir, (void **)&pNewDir);
1732 if (RT_SUCCESS(rc))
1733 {
1734 pNewDir->fNoMoreFiles = false;
1735 pNewDir->pVol = pThis;
1736 pNewDir->pInode = pInode;
1737 }
1738 }
1739 else
1740 rc = VERR_VFS_BOGUS_FORMAT;
1741
1742 if (RT_FAILURE(rc))
1743 rtFsXfsInode_Release(pThis, pInode);
1744 }
1745
1746 return rc;
1747}
1748
1749
1750
1751/*
1752 *
1753 * Volume level code.
1754 * Volume level code.
1755 * Volume level code.
1756 *
1757 */
1758
1759static DECLCALLBACK(int) rtFsXfsVolAgTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
1760{
1761 RT_NOREF(pvUser);
1762
1763 PRTFSXFSAG pAg = (PRTFSXFSAG)pCore;
1764 Assert(!pAg->cRefs);
1765 RTMemFree(pAg);
1766 return VINF_SUCCESS;
1767}
1768
1769
1770static DECLCALLBACK(int) rtFsXfsVolInodeTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
1771{
1772 RT_NOREF(pvUser);
1773
1774 PRTFSXFSINODE pInode = (PRTFSXFSINODE)pCore;
1775 Assert(!pInode->cRefs);
1776 RTMemFree(pInode);
1777 return VINF_SUCCESS;
1778}
1779
1780
1781static DECLCALLBACK(int) rtFsXfsVolBlockTreeDestroy(PAVLU64NODECORE pCore, void *pvUser)
1782{
1783 RT_NOREF(pvUser);
1784
1785 PRTFSXFSBLOCKENTRY pBlock = (PRTFSXFSBLOCKENTRY)pCore;
1786 Assert(!pBlock->cRefs);
1787 RTMemFree(pBlock);
1788 return VINF_SUCCESS;
1789}
1790
1791
1792/**
1793 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1794 */
1795static DECLCALLBACK(int) rtFsXfsVol_Close(void *pvThis)
1796{
1797 PRTFSXFSVOL pThis = (PRTFSXFSVOL)pvThis;
1798
1799 /* Destroy the block group tree. */
1800 RTAvlU32Destroy(&pThis->AgRoot, rtFsXfsVolAgTreeDestroy, pThis);
1801 pThis->AgRoot = NULL;
1802 RTListInit(&pThis->LstAgLru);
1803
1804 /* Destroy the inode tree. */
1805 RTAvlU32Destroy(&pThis->InodeRoot, rtFsXfsVolInodeTreeDestroy, pThis);
1806 pThis->InodeRoot = NULL;
1807 RTListInit(&pThis->LstInodeLru);
1808
1809 /* Destroy the block cache tree. */
1810 RTAvlU64Destroy(&pThis->BlockRoot, rtFsXfsVolBlockTreeDestroy, pThis);
1811 pThis->BlockRoot = NULL;
1812 RTListInit(&pThis->LstBlockLru);
1813
1814 /*
1815 * Backing file and handles.
1816 */
1817 RTVfsFileRelease(pThis->hVfsBacking);
1818 pThis->hVfsBacking = NIL_RTVFSFILE;
1819 pThis->hVfsSelf = NIL_RTVFS;
1820
1821 return VINF_SUCCESS;
1822}
1823
1824
1825/**
1826 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1827 */
1828static DECLCALLBACK(int) rtFsXfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1829{
1830 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
1831 return VERR_WRONG_TYPE;
1832}
1833
1834
1835/**
1836 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
1837 */
1838static DECLCALLBACK(int) rtFsXfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
1839{
1840 PRTFSXFSVOL pThis = (PRTFSXFSVOL)pvThis;
1841 int rc = rtFsXfsVol_OpenDirByInode(pThis, 0 /** @todo */, phVfsDir);
1842 LogFlowFunc(("returns %Rrc\n", rc));
1843 return rc;
1844}
1845
1846
1847/**
1848 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
1849 */
1850static DECLCALLBACK(int) rtFsXfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
1851{
1852 RT_NOREF(pvThis, off, cb, pfUsed);
1853 return VERR_NOT_IMPLEMENTED;
1854}
1855
1856
1857DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsXfsVolOps =
1858{
1859 /* .Obj = */
1860 {
1861 /* .uVersion = */ RTVFSOBJOPS_VERSION,
1862 /* .enmType = */ RTVFSOBJTYPE_VFS,
1863 /* .pszName = */ "XfsVol",
1864 /* .pfnClose = */ rtFsXfsVol_Close,
1865 /* .pfnQueryInfo = */ rtFsXfsVol_QueryInfo,
1866 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
1867 },
1868 /* .uVersion = */ RTVFSOPS_VERSION,
1869 /* .fFeatures = */ 0,
1870 /* .pfnOpenRoot = */ rtFsXfsVol_OpenRoot,
1871 /* .pfnQueryRangeState = */ rtFsXfsVol_QueryRangeState,
1872 /* .uEndMarker = */ RTVFSOPS_VERSION
1873};
1874
1875
1876
1877/**
1878 * Loads and parses the superblock of the filesystem.
1879 *
1880 * @returns IPRT status code.
1881 * @param pThis The XFS volume instance.
1882 * @param pErrInfo Where to return additional error info.
1883 */
1884static int rtFsXfsVolLoadAndParseSuperblock(PRTFSXFSVOL pThis, PRTERRINFO pErrInfo)
1885{
1886 int rc = VINF_SUCCESS;
1887 XFSSUPERBLOCK Sb;
1888 rc = RTVfsFileReadAt(pThis->hVfsBacking, XFS_SB_OFFSET, &Sb, sizeof(XFSSUPERBLOCK), NULL);
1889 if (RT_FAILURE(rc))
1890 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
1891
1892 /* Validate the superblock. */
1893 if (RT_BE2H_U32(Sb.u32Magic) != XFS_SB_MAGIC)
1894 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not XFS - Signature mismatch: %RX32", RT_BE2H_U32(Sb.u32Magic));
1895
1896#ifdef LOG_ENABLED
1897 rtFsXfsSb_Log(&Sb);
1898#endif
1899
1900 /** @todo */
1901 return rc;
1902}
1903
1904
1905RTDECL(int) RTFsXfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fXfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
1906{
1907 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
1908 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
1909 AssertReturn(!fXfsFlags, VERR_INVALID_FLAGS);
1910
1911 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
1912 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1913
1914 /*
1915 * Create a VFS instance and initialize the data so rtFsExtVol_Close works.
1916 */
1917 RTVFS hVfs;
1918 PRTFSXFSVOL pThis;
1919 int rc = RTVfsNew(&g_rtFsXfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
1920 if (RT_SUCCESS(rc))
1921 {
1922 pThis->hVfsBacking = hVfsFileIn;
1923 pThis->hVfsSelf = hVfs;
1924 pThis->fMntFlags = fMntFlags;
1925 pThis->fXfsFlags = fXfsFlags;
1926 pThis->AgRoot = NULL;
1927 pThis->InodeRoot = NULL;
1928 pThis->BlockRoot = NULL;
1929 pThis->cbAgs = 0;
1930 pThis->cbInodes = 0;
1931 pThis->cbBlocks = 0;
1932 RTListInit(&pThis->LstAgLru);
1933 RTListInit(&pThis->LstInodeLru);
1934 RTListInit(&pThis->LstBlockLru);
1935
1936 rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
1937 if (RT_SUCCESS(rc))
1938 {
1939 rc = rtFsXfsVolLoadAndParseSuperblock(pThis, pErrInfo);
1940 if (RT_SUCCESS(rc))
1941 {
1942 *phVfs = hVfs;
1943 return VINF_SUCCESS;
1944 }
1945 }
1946
1947 RTVfsRelease(hVfs);
1948 *phVfs = NIL_RTVFS;
1949 }
1950 else
1951 RTVfsFileRelease(hVfsFileIn);
1952
1953 return rc;
1954}
1955
1956
1957/**
1958 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
1959 */
1960static DECLCALLBACK(int) rtVfsChainXfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
1961 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
1962{
1963 RT_NOREF(pProviderReg);
1964
1965 /*
1966 * Basic checks.
1967 */
1968 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
1969 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
1970 if ( pElement->enmType != RTVFSOBJTYPE_VFS
1971 && pElement->enmType != RTVFSOBJTYPE_DIR)
1972 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
1973 if (pElement->cArgs > 1)
1974 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
1975
1976 /*
1977 * Parse the flag if present, save in pElement->uProvider.
1978 */
1979 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
1980 if (pElement->cArgs > 0)
1981 {
1982 const char *psz = pElement->paArgs[0].psz;
1983 if (*psz)
1984 {
1985 if (!strcmp(psz, "ro"))
1986 fReadOnly = true;
1987 else if (!strcmp(psz, "rw"))
1988 fReadOnly = false;
1989 else
1990 {
1991 *poffError = pElement->paArgs[0].offSpec;
1992 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
1993 }
1994 }
1995 }
1996
1997 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
1998 return VINF_SUCCESS;
1999}
2000
2001
2002/**
2003 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2004 */
2005static DECLCALLBACK(int) rtVfsChainXfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2006 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2007 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2008{
2009 RT_NOREF(pProviderReg, pSpec, poffError);
2010
2011 int rc;
2012 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2013 if (hVfsFileIn != NIL_RTVFSFILE)
2014 {
2015 RTVFS hVfs;
2016 rc = RTFsXfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
2017 RTVfsFileRelease(hVfsFileIn);
2018 if (RT_SUCCESS(rc))
2019 {
2020 *phVfsObj = RTVfsObjFromVfs(hVfs);
2021 RTVfsRelease(hVfs);
2022 if (*phVfsObj != NIL_RTVFSOBJ)
2023 return VINF_SUCCESS;
2024 rc = VERR_VFS_CHAIN_CAST_FAILED;
2025 }
2026 }
2027 else
2028 rc = VERR_VFS_CHAIN_CAST_FAILED;
2029 return rc;
2030}
2031
2032
2033/**
2034 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2035 */
2036static DECLCALLBACK(bool) rtVfsChainXfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2037 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2038 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2039{
2040 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2041 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2042 || !pReuseElement->paArgs[0].uProvider)
2043 return true;
2044 return false;
2045}
2046
2047
2048/** VFS chain element 'xfs'. */
2049static RTVFSCHAINELEMENTREG g_rtVfsChainXfsVolReg =
2050{
2051 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2052 /* fReserved = */ 0,
2053 /* pszName = */ "xfs",
2054 /* ListEntry = */ { NULL, NULL },
2055 /* pszHelp = */ "Open a XFS file system, requires a file object on the left side.\n"
2056 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
2057 /* pfnValidate = */ rtVfsChainXfsVol_Validate,
2058 /* pfnInstantiate = */ rtVfsChainXfsVol_Instantiate,
2059 /* pfnCanReuseElement = */ rtVfsChainXfsVol_CanReuseElement,
2060 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2061};
2062
2063RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainXfsVolReg, rtVfsChainXfsVolReg);
2064
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