VirtualBox

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

Last change on this file was 100908, checked in by vboxsync, 9 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use