VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/extvfs.cpp@ 100594

Last change on this file since 100594 was 99739, checked in by vboxsync, 21 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.4 KB
Line 
1/* $Id: extvfs.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
2/** @file
3 * IPRT - Ext2/3/4 Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2012-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/ext.h>
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/** The maximum block group cache size (in bytes). */
62#if ARCH_BITS >= 64
63# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _512K
64#else
65# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _128K
66#endif
67/** The maximum inode cache size (in bytes). */
68#if ARCH_BITS >= 64
69# define RTFSEXT_MAX_INODE_CACHE_SIZE _512K
70#else
71# define RTFSEXT_MAX_INODE_CACHE_SIZE _128K
72#endif
73/** The maximum extent/block map cache size (in bytes). */
74#if ARCH_BITS >= 64
75# define RTFSEXT_MAX_BLOCK_CACHE_SIZE _512K
76#else
77# define RTFSEXT_MAX_BLOCK_CACHE_SIZE _128K
78#endif
79
80/** All supported incompatible features. */
81#define RTFSEXT_INCOMPAT_FEATURES_SUPP ( EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE | EXT_SB_FEAT_INCOMPAT_EXTENTS | EXT_SB_FEAT_INCOMPAT_64BIT \
82 | EXT_SB_FEAT_INCOMPAT_FLEX_BG)
83
84
85/*********************************************************************************************************************************
86* Structures and Typedefs *
87*********************************************************************************************************************************/
88/** Pointer to the ext filesystem data. */
89typedef struct RTFSEXTVOL *PRTFSEXTVOL;
90
91
92/**
93 * Cached block group descriptor data.
94 */
95typedef struct RTFSEXTBLKGRP
96{
97 /** AVL tree node, indexed by the block group number. */
98 AVLU32NODECORE Core;
99 /** List node for the LRU list used for eviction. */
100 RTLISTNODE NdLru;
101 /** Reference counter. */
102 volatile uint32_t cRefs;
103 /** Block number where the inode table is store. */
104 uint64_t iBlockInodeTbl;
105 /** Pointer to the inode bitmap. */
106 uint8_t *pabInodeBitmap;
107 /** Block bitmap - variable in size (depends on the block size
108 * and number of blocks per group). */
109 uint8_t abBlockBitmap[1];
110} RTFSEXTBLKGRP;
111/** Pointer to block group descriptor data. */
112typedef RTFSEXTBLKGRP *PRTFSEXTBLKGRP;
113
114
115/**
116 * In-memory inode.
117 */
118typedef struct RTFSEXTINODE
119{
120 /** AVL tree node, indexed by the inode number. */
121 AVLU32NODECORE Core;
122 /** List node for the inode LRU list used for eviction. */
123 RTLISTNODE NdLru;
124 /** Reference counter. */
125 volatile uint32_t cRefs;
126 /** Byte offset in the backing file where the inode is stored.. */
127 uint64_t offInode;
128 /** Inode data. */
129 RTFSOBJINFO ObjInfo;
130 /** Inode flags (copied from the on disk inode). */
131 uint32_t fFlags;
132 /** Copy of the block map/extent tree. */
133 uint32_t aiBlocks[EXT_INODE_BLOCK_ENTRIES];
134} RTFSEXTINODE;
135/** Pointer to an in-memory inode. */
136typedef RTFSEXTINODE *PRTFSEXTINODE;
137
138
139/**
140 * Block cache entry.
141 */
142typedef struct RTFSEXTBLOCKENTRY
143{
144 /** AVL tree node, indexed by the filesystem block number. */
145 AVLU64NODECORE Core;
146 /** List node for the inode LRU list used for eviction. */
147 RTLISTNODE NdLru;
148 /** Reference counter. */
149 volatile uint32_t cRefs;
150 /** The block data. */
151 uint8_t abData[1];
152} RTFSEXTBLOCKENTRY;
153/** Pointer to a block cache entry. */
154typedef RTFSEXTBLOCKENTRY *PRTFSEXTBLOCKENTRY;
155
156
157/**
158 * Open directory instance.
159 */
160typedef struct RTFSEXTDIR
161{
162 /** Volume this directory belongs to. */
163 PRTFSEXTVOL pVol;
164 /** The underlying inode structure. */
165 PRTFSEXTINODE pInode;
166 /** Set if we've reached the end of the directory enumeration. */
167 bool fNoMoreFiles;
168 /** Current offset into the directory where the next entry should be read. */
169 uint64_t offEntry;
170 /** Next entry index (for logging purposes). */
171 uint32_t idxEntry;
172} RTFSEXTDIR;
173/** Pointer to an open directory instance. */
174typedef RTFSEXTDIR *PRTFSEXTDIR;
175
176
177/**
178 * Open file instance.
179 */
180typedef struct RTFSEXTFILE
181{
182 /** Volume this directory belongs to. */
183 PRTFSEXTVOL pVol;
184 /** The underlying inode structure. */
185 PRTFSEXTINODE pInode;
186 /** Current offset into the file for I/O. */
187 RTFOFF offFile;
188} RTFSEXTFILE;
189/** Pointer to an open file instance. */
190typedef RTFSEXTFILE *PRTFSEXTFILE;
191
192
193/**
194 * Ext2/3/4 filesystem volume.
195 */
196typedef struct RTFSEXTVOL
197{
198 /** Handle to itself. */
199 RTVFS hVfsSelf;
200 /** The file, partition, or whatever backing the ext volume. */
201 RTVFSFILE hVfsBacking;
202 /** The size of the backing thingy. */
203 uint64_t cbBacking;
204
205 /** RTVFSMNT_F_XXX. */
206 uint32_t fMntFlags;
207 /** RTFSEXTVFS_F_XXX (currently none defined). */
208 uint32_t fExtFlags;
209
210 /** Flag whether the filesystem is 64bit. */
211 bool f64Bit;
212 /** Size of one block. */
213 size_t cbBlock;
214 /** Number of bits to shift left for fast conversion of block numbers to offsets. */
215 uint32_t cBlockShift;
216 /** Number of blocks in one group. */
217 uint32_t cBlocksPerGroup;
218 /** Number of inodes in each block group. */
219 uint32_t cInodesPerGroup;
220 /** Number of blocks groups in the volume. */
221 uint32_t cBlockGroups;
222 /** Size of the block bitmap. */
223 size_t cbBlockBitmap;
224 /** Size of the inode bitmap. */
225 size_t cbInodeBitmap;
226 /** Size of block group descriptor. */
227 size_t cbBlkGrpDesc;
228 /** Size of an inode. */
229 size_t cbInode;
230
231 /** Incompatible features selected for this filesystem. */
232 uint32_t fFeaturesIncompat;
233
234 /** @name Block group cache.
235 * @{ */
236 /** LRU list anchor. */
237 RTLISTANCHOR LstBlockGroupLru;
238 /** Root of the cached block group tree. */
239 AVLU32TREE BlockGroupRoot;
240 /** Size of the cached block groups. */
241 size_t cbBlockGroups;
242 /** @} */
243
244 /** @name Inode cache.
245 * @{ */
246 /** LRU list anchor for the inode cache. */
247 RTLISTANCHOR LstInodeLru;
248 /** Root of the cached inode tree. */
249 AVLU32TREE InodeRoot;
250 /** Size of the cached inodes. */
251 size_t cbInodes;
252 /** @} */
253
254 /** @name Block cache.
255 * @{ */
256 /** LRU list anchor for the block cache. */
257 RTLISTANCHOR LstBlockLru;
258 /** Root of the cached block tree. */
259 AVLU64TREE BlockRoot;
260 /** Size of cached blocks. */
261 size_t cbBlocks;
262 /** @} */
263} RTFSEXTVOL;
264
265
266
267/*********************************************************************************************************************************
268* Internal Functions *
269*********************************************************************************************************************************/
270static int rtFsExtVol_OpenDirByInode(PRTFSEXTVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir);
271
272#ifdef LOG_ENABLED
273
274/**
275 * Logs the ext filesystem superblock.
276 *
277 * @param pSb Pointer to the superblock.
278 */
279static void rtFsExtSb_Log(PCEXTSUPERBLOCK pSb)
280{
281 if (LogIs2Enabled())
282 {
283 RTTIMESPEC Spec;
284 char sz[80];
285
286 Log2(("EXT: Superblock:\n"));
287 Log2(("EXT: cInodesTotal %RU32\n", RT_LE2H_U32(pSb->cInodesTotal)));
288 Log2(("EXT: cBlocksTotalLow %RU32\n", RT_LE2H_U32(pSb->cBlocksTotalLow)));
289 Log2(("EXT: cBlocksRsvdForSuperUserLow %RU32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserLow)));
290 Log2(("EXT: cBlocksFreeLow %RU32\n", RT_LE2H_U32(pSb->cBlocksFreeLow)));
291 Log2(("EXT: cInodesFree %RU32\n", RT_LE2H_U32(pSb->cInodesFree)));
292 Log2(("EXT: iBlockOfSuperblock %RU32\n", RT_LE2H_U32(pSb->iBlockOfSuperblock)));
293 Log2(("EXT: cLogBlockSize %RU32\n", RT_LE2H_U32(pSb->cLogBlockSize)));
294 Log2(("EXT: cLogClusterSize %RU32\n", RT_LE2H_U32(pSb->cLogClusterSize)));
295 Log2(("EXT: cBlocksPerGroup %RU32\n", RT_LE2H_U32(pSb->cBlocksPerGroup)));
296 Log2(("EXT: cClustersPerBlockGroup %RU32\n", RT_LE2H_U32(pSb->cClustersPerBlockGroup)));
297 Log2(("EXT: cInodesPerBlockGroup %RU32\n", RT_LE2H_U32(pSb->cInodesPerBlockGroup)));
298 Log2(("EXT: u32LastMountTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastMountTime),
299 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastMountTime)), sz, sizeof(sz))));
300 Log2(("EXT: u32LastWrittenTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastWrittenTime),
301 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastWrittenTime)), sz, sizeof(sz))));
302 Log2(("EXT: cMountsSinceLastCheck %RU16\n", RT_LE2H_U32(pSb->cMountsSinceLastCheck)));
303 Log2(("EXT: cMaxMountsUntilCheck %RU16\n", RT_LE2H_U32(pSb->cMaxMountsUntilCheck)));
304 Log2(("EXT: u16Signature %#RX16\n", RT_LE2H_U32(pSb->u16Signature)));
305 Log2(("EXT: u16FilesystemState %#RX16\n", RT_LE2H_U32(pSb->u16FilesystemState)));
306 Log2(("EXT: u16ActionOnError %#RX16\n", RT_LE2H_U32(pSb->u16ActionOnError)));
307 Log2(("EXT: u16RevLvlMinor %#RX16\n", RT_LE2H_U32(pSb->u16RevLvlMinor)));
308 Log2(("EXT: u32LastCheckTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastCheckTime),
309 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastCheckTime)), sz, sizeof(sz))));
310 Log2(("EXT: u32CheckInterval %RU32\n", RT_LE2H_U32(pSb->u32CheckInterval)));
311 Log2(("EXT: u32OsIdCreator %#RX32\n", RT_LE2H_U32(pSb->u32OsIdCreator)));
312 Log2(("EXT: u32RevLvl %#RX32\n", RT_LE2H_U32(pSb->u32RevLvl)));
313 Log2(("EXT: u16UidReservedBlocks %#RX16\n", RT_LE2H_U32(pSb->u16UidReservedBlocks)));
314 Log2(("EXT: u16GidReservedBlocks %#RX16\n", RT_LE2H_U32(pSb->u16GidReservedBlocks)));
315 if (RT_LE2H_U32(pSb->u32RevLvl) == EXT_SB_REV_V2_DYN_INODE_SZ)
316 {
317 Log2(("EXT: iFirstInodeNonRsvd %#RX32\n", RT_LE2H_U32(pSb->iFirstInodeNonRsvd)));
318 Log2(("EXT: cbInode %#RX16\n", RT_LE2H_U32(pSb->cbInode)));
319 Log2(("EXT: iBlkGrpSb %#RX16\n", RT_LE2H_U32(pSb->iBlkGrpSb)));
320 Log2(("EXT: fFeaturesCompat %#RX32%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompat),
321 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_PREALLOC ? " dir-prealloc" : "",
322 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_IMAGIC_INODES ? " imagic-inode" : "",
323 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_HAS_JOURNAL ? " has-journal" : "",
324 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXT_ATTR ? " ext-attrs" : "",
325 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_RESIZE_INODE ? " resize-inode" : "",
326 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_INDEX ? " dir-index" : "",
327 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_LAZY_BG ? " lazy-bg" : "",
328 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_INODE ? " excl-inode" : "",
329 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_BITMAP ? " excl-bitmap" : "",
330 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_SPARSE_SUPER2 ? " sparse-super2" : ""));
331 Log2(("EXT: fFeaturesIncompat %#RX32%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesIncompat),
332 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_COMPRESSION ? " compression" : "",
333 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE ? " dir-filetype" : "",
334 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_RECOVER ? " recovery" : "",
335 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_JOURNAL_DEV ? " journal-dev" : "",
336 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_META_BG ? " meta-bg" : "",
337 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXTENTS ? " extents" : "",
338 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_64BIT ? " 64bit" : "",
339 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_MMP ? " mmp" : "",
340 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_FLEX_BG ? " flex-bg" : "",
341 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXT_ATTR_INODE ? " extattr-inode" : "",
342 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIRDATA ? " dir-data" : "",
343 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_CSUM_SEED ? " csum-seed" : "",
344 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_LARGE_DIR ? " large-dir" : "",
345 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_INLINE_DATA ? " inline-data" : "",
346 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_ENCRYPT ? " encrypt" : ""));
347 Log2(("EXT: fFeaturesCompatRo %#RX32%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompatRo),
348 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_SPARSE_SUPER ? " sparse-super" : "",
349 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_LARGE_FILE ? " large-file" : "",
350 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BTREE_DIR ? " btree-dir" : "",
351 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HUGE_FILE ? " huge-file" : "",
352 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_GDT_CHSKUM ? " gdt-chksum" : "",
353 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_DIR_NLINK ? " dir-nlink" : "",
354 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_EXTRA_INODE_SZ ? " extra-inode" : "",
355 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HAS_SNAPSHOTS ? " snapshots" : "",
356 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_QUOTA ? " quota" : "",
357 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BIGALLOC ? " big-alloc" : "",
358 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_METADATA_CHKSUM ? " meta-chksum" : "",
359 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_REPLICA ? " replica" : "",
360 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_READONLY ? " ro" : "",
361 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_PROJECT ? " project" : ""));
362 Log2(("EXT: au8Uuid <todo>\n"));
363 Log2(("EXT: achVolumeName %16s\n", &pSb->achVolumeName[0]));
364 Log2(("EXT: achLastMounted %64s\n", &pSb->achLastMounted[0]));
365 Log2(("EXT: u32AlgoUsageBitmap %#RX32\n", RT_LE2H_U32(pSb->u32AlgoUsageBitmap)));
366 Log2(("EXT: cBlocksPrealloc %RU8\n", pSb->cBlocksPrealloc));
367 Log2(("EXT: cBlocksPreallocDirectory %RU8\n", pSb->cBlocksPreallocDirectory));
368 Log2(("EXT: cGdtEntriesRsvd %RU16\n", pSb->cGdtEntriesRsvd));
369 Log2(("EXT: au8JournalUuid <todo>\n"));
370 Log2(("EXT: iJournalInode %#RX32\n", RT_LE2H_U32(pSb->iJournalInode)));
371 Log2(("EXT: u32JournalDev %#RX32\n", RT_LE2H_U32(pSb->u32JournalDev)));
372 Log2(("EXT: u32LastOrphan %#RX32\n", RT_LE2H_U32(pSb->u32LastOrphan)));
373 Log2(("EXT: au32HashSeedHtree[0] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[0])));
374 Log2(("EXT: au32HashSeedHtree[1] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[1])));
375 Log2(("EXT: au32HashSeedHtree[2] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[2])));
376 Log2(("EXT: au32HashSeedHtree[3] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[3])));
377 Log2(("EXT: u8HashVersionDef %#RX8\n", pSb->u8HashVersionDef));
378 Log2(("EXT: u8JnlBackupType %#RX8\n", pSb->u8JnlBackupType));
379 Log2(("EXT: cbGroupDesc %RU16\n", RT_LE2H_U16(pSb->cbGroupDesc)));
380 Log2(("EXT: fMntOptsDef %#RX32\n", RT_LE2H_U32(pSb->fMntOptsDef)));
381 Log2(("EXT: iFirstMetaBg %#RX32\n", RT_LE2H_U32(pSb->iFirstMetaBg)));
382 Log2(("EXT: u32TimeFsCreation %#RX32 %s\n", RT_LE2H_U32(pSb->u32TimeFsCreation),
383 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32TimeFsCreation)), sz, sizeof(sz))));
384 for (unsigned i = 0; i < RT_ELEMENTS(pSb->au32JnlBlocks); i++)
385 Log2(("EXT: au32JnlBlocks[%u] %#RX32\n", i, RT_LE2H_U32(pSb->au32JnlBlocks[i])));
386 Log2(("EXT: cBlocksTotalHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksTotalHigh)));
387 Log2(("EXT: cBlocksRsvdForSuperUserHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserHigh)));
388 Log2(("EXT: cBlocksFreeHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksFreeHigh)));
389 Log2(("EXT: cbInodesExtraMin %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
390 Log2(("EXT: cbNewInodesRsv %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
391 Log2(("EXT: fFlags %#RX32\n", RT_LE2H_U32(pSb->fFlags)));
392 Log2(("EXT: cRaidStride %RU16\n", RT_LE2H_U16(pSb->cRaidStride)));
393 Log2(("EXT: cSecMmpInterval %RU16\n", RT_LE2H_U16(pSb->cSecMmpInterval)));
394 Log2(("EXT: iMmpBlock %#RX64\n", RT_LE2H_U64(pSb->iMmpBlock)));
395 Log2(("EXT: cRaidStrideWidth %#RX32\n", RT_LE2H_U32(pSb->cRaidStrideWidth)));
396 Log2(("EXT: cLogGroupsPerFlex %RU8\n", pSb->cLogGroupsPerFlex));
397 Log2(("EXT: u8ChksumType %RX8\n", pSb->u8ChksumType));
398 Log2(("EXT: cKbWritten %#RX64\n", RT_LE2H_U64(pSb->cKbWritten)));
399 Log2(("EXT: iSnapshotInode %#RX32\n", RT_LE2H_U32(pSb->iSnapshotInode)));
400 Log2(("EXT: iSnapshotId %#RX32\n", RT_LE2H_U32(pSb->iSnapshotId)));
401 Log2(("EXT: cSnapshotRsvdBlocks %#RX64\n", RT_LE2H_U64(pSb->cSnapshotRsvdBlocks)));
402 Log2(("EXT: iSnapshotListInode %#RX32\n", RT_LE2H_U32(pSb->iSnapshotListInode)));
403 Log2(("EXT: cErrorsSeen %#RX32\n", RT_LE2H_U32(pSb->cErrorsSeen)));
404 Log2(("EXT: [...]\n")); /** @todo Missing fields if becoming interesting. */
405 Log2(("EXT: iInodeLostFound %#RX32\n", RT_LE2H_U32(pSb->iInodeLostFound)));
406 Log2(("EXT: iInodeProjQuota %#RX32\n", RT_LE2H_U32(pSb->iInodeProjQuota)));
407 Log2(("EXT: u32ChksumSeed %#RX32\n", RT_LE2H_U32(pSb->u32ChksumSeed)));
408 Log2(("EXT: [...]\n")); /** @todo Missing fields if becoming interesting. */
409 Log2(("EXT: u32Chksum %#RX32\n", RT_LE2H_U32(pSb->u32Chksum)));
410 }
411 }
412}
413
414
415/**
416 * Logs a ext filesystem block group descriptor.
417 *
418 * @param pThis The ext volume instance.
419 * @param iBlockGroup Block group number.
420 * @param pBlockGroup Pointer to the block group.
421 */
422static void rtFsExtBlockGroup_Log(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PCEXTBLOCKGROUPDESC pBlockGroup)
423{
424 if (LogIs2Enabled())
425 {
426 uint64_t iBlockStart = (uint64_t)iBlockGroup * pThis->cBlocksPerGroup;
427 Log2(("EXT: Block group %#RX32 (blocks %#RX64 to %#RX64):\n",
428 iBlockGroup, iBlockStart, iBlockStart + pThis->cBlocksPerGroup - 1));
429 Log2(("EXT: offBlockBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offBlockBitmapLow)));
430 Log2(("EXT: offInodeBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeBitmapLow)));
431 Log2(("EXT: offInodeTableLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeTableLow)));
432 Log2(("EXT: cBlocksFreeLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cBlocksFreeLow)));
433 Log2(("EXT: cInodesFreeLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodesFreeLow)));
434 Log2(("EXT: cDirectoriesLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cDirectoriesLow)));
435 Log2(("EXT: fFlags %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.fFlags)));
436 Log2(("EXT: offSnapshotExclBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offSnapshotExclBitmapLow)));
437 Log2(("EXT: u16ChksumBlockBitmapLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumBlockBitmapLow)));
438 Log2(("EXT: u16ChksumInodeBitmapLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumInodeBitmapLow)));
439 Log2(("EXT: cInodeTblUnusedLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodeTblUnusedLow)));
440 Log2(("EXT: u16Chksum %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16Chksum)));
441 if (pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
442 {
443 Log2(("EXT: offBlockBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offBlockBitmapHigh)));
444 Log2(("EXT: offInodeBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeBitmapHigh)));
445 Log2(("EXT: offInodeTableHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeTableHigh)));
446 Log2(("EXT: cBlocksFreeHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cBlocksFreeHigh)));
447 Log2(("EXT: cInodesFreeHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodesFreeHigh)));
448 Log2(("EXT: cDirectoriesHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cDirectoriesHigh)));
449 Log2(("EXT: cInodeTblUnusedHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodeTblUnusedHigh)));
450 Log2(("EXT: offSnapshotExclBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offSnapshotExclBitmapHigh)));
451 Log2(("EXT: u16ChksumBlockBitmapHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumBlockBitmapHigh)));
452 Log2(("EXT: u16ChksumInodeBitmapHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumInodeBitmapHigh)));
453 }
454 }
455}
456
457
458/**
459 * Logs a ext filesystem inode.
460 *
461 * @param pThis The ext volume instance.
462 * @param iInode Inode number.
463 * @param pInode Pointer to the inode.
464 */
465static void rtFsExtInode_Log(PRTFSEXTVOL pThis, uint32_t iInode, PCEXTINODECOMB pInode)
466{
467 if (LogIs2Enabled())
468 {
469 RTTIMESPEC Spec;
470 char sz[80];
471
472 Log2(("EXT: Inode %#RX32:\n", iInode));
473 Log2(("EXT: fMode %#RX16\n", RT_LE2H_U16(pInode->Core.fMode)));
474 Log2(("EXT: uUidLow %#RX16\n", RT_LE2H_U16(pInode->Core.uUidLow)));
475 Log2(("EXT: cbSizeLow %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeLow)));
476 Log2(("EXT: u32TimeLastAccess %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastAccess),
477 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastAccess)), sz, sizeof(sz))));
478 Log2(("EXT: u32TimeLastChange %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastChange),
479 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastChange)), sz, sizeof(sz))));
480 Log2(("EXT: u32TimeLastModification %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastModification),
481 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastModification)), sz, sizeof(sz))));
482 Log2(("EXT: u32TimeDeletion %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeDeletion),
483 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeDeletion)), sz, sizeof(sz))));
484 Log2(("EXT: uGidLow %#RX16\n", RT_LE2H_U16(pInode->Core.uGidLow)));
485 Log2(("EXT: cHardLinks %#RU16\n", RT_LE2H_U16(pInode->Core.cHardLinks)));
486 Log2(("EXT: cBlocksLow %#RX32\n", RT_LE2H_U32(pInode->Core.cBlocksLow)));
487 Log2(("EXT: fFlags %#RX32\n", RT_LE2H_U32(pInode->Core.fFlags)));
488 Log2(("EXT: Osd1.u32LnxVersion %#RX32\n", RT_LE2H_U32(pInode->Core.Osd1.u32LnxVersion)));
489 for (unsigned i = 0; i < RT_ELEMENTS(pInode->Core.au32Block); i++)
490 Log2(("EXT: au32Block[%u] %#RX32\n", i, RT_LE2H_U32(pInode->Core.au32Block[i])));
491 Log2(("EXT: u32Version %#RX32\n", RT_LE2H_U32(pInode->Core.u32Version)));
492 Log2(("EXT: offExtAttrLow %#RX32\n", RT_LE2H_U32(pInode->Core.offExtAttrLow)));
493 Log2(("EXT: cbSizeHigh %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeHigh)));
494 Log2(("EXT: u32FragmentAddrObs %#RX32\n", RT_LE2H_U32(pInode->Core.u32FragmentAddrObs)));
495 Log2(("EXT: Osd2.Lnx.cBlocksHigh %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.cBlocksHigh)));
496 Log2(("EXT: Osd2.Lnx.offExtAttrHigh %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.offExtAttrHigh)));
497 Log2(("EXT: Osd2.Lnx.uUidHigh %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uUidHigh)));
498 Log2(("EXT: Osd2.Lnx.uGidHigh %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uGidHigh)));
499 Log2(("EXT: Osd2.Lnx.u16ChksumLow %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.u16ChksumLow)));
500
501 if (pThis->cbInode >= sizeof(EXTINODECOMB))
502 {
503 Log2(("EXT: cbInodeExtra %#RU16\n", RT_LE2H_U16(pInode->Extra.cbInodeExtra)));
504 Log2(("EXT: u16ChksumHigh %#RX16\n", RT_LE2H_U16(pInode->Extra.u16ChksumHigh)));
505 Log2(("EXT: u32ExtraTimeLastChange %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastChange)));
506 Log2(("EXT: u32ExtraTimeLastModification %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastModification)));
507 Log2(("EXT: u32ExtraTimeLastAccess %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastAccess)));
508 Log2(("EXT: u32TimeCreation %#RX32 %s\n", RT_LE2H_U32(pInode->Extra.u32TimeCreation),
509 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Extra.u32TimeCreation)), sz, sizeof(sz))));
510 Log2(("EXT: u32ExtraTimeCreation %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeCreation)));
511 Log2(("EXT: u32VersionHigh %#RX32\n", RT_LE2H_U16(pInode->Extra.u32VersionHigh)));
512 Log2(("EXT: u32ProjectId %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ProjectId)));
513 }
514 }
515}
516
517
518/**
519 * Logs a ext filesystem directory entry.
520 *
521 * @param pThis The ext volume instance.
522 * @param idxDirEntry Directory entry index number.
523 * @param pDirEntry The directory entry.
524 */
525static void rtFsExtDirEntry_Log(PRTFSEXTVOL pThis, uint32_t idxDirEntry, PCEXTDIRENTRYEX pDirEntry)
526{
527 if (LogIs2Enabled())
528 {
529 int cbName = 0;
530
531 Log2(("EXT: Directory entry %#RX32:\n", idxDirEntry));
532 Log2(("EXT: iInodeRef %#RX32\n", RT_LE2H_U32(pDirEntry->Core.iInodeRef)));
533 Log2(("EXT: cbRecord %#RX32\n", RT_LE2H_U32(pDirEntry->Core.cbRecord)));
534 if (pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE)
535 {
536 Log2(("EXT: cbName %#RU8\n", pDirEntry->Core.u.v2.cbName));
537 Log2(("EXT: uType %#RX8\n", pDirEntry->Core.u.v2.uType));
538 cbName = pDirEntry->Core.u.v2.cbName;
539 }
540 else
541 {
542 Log2(("EXT: cbName %#RU16\n", RT_LE2H_U16(pDirEntry->Core.u.v1.cbName)));
543 cbName = RT_LE2H_U16(pDirEntry->Core.u.v1.cbName);
544 }
545 Log2(("EXT: achName %*s\n", cbName, &pDirEntry->Core.achName[0]));
546 }
547}
548
549
550/**
551 * Logs an extent header.
552 *
553 * @param pExtentHdr The extent header node.
554 */
555static void rtFsExtExtentHdr_Log(PCEXTEXTENTHDR pExtentHdr)
556{
557 if (LogIs2Enabled())
558 {
559 Log2(("EXT: Extent header:\n"));
560 Log2(("EXT: u16Magic %#RX16\n", RT_LE2H_U32(pExtentHdr->u16Magic)));
561 Log2(("EXT: cEntries %#RX16\n", RT_LE2H_U32(pExtentHdr->cEntries)));
562 Log2(("EXT: cMax %#RX16\n", RT_LE2H_U32(pExtentHdr->cMax)));
563 Log2(("EXT: uDepth %#RX16\n", RT_LE2H_U32(pExtentHdr->uDepth)));
564 Log2(("EXT: cGeneration %#RX32\n", RT_LE2H_U32(pExtentHdr->cGeneration)));
565 }
566}
567
568
569/**
570 * Logs an extent index node.
571 *
572 * @param pExtentIdx The extent index node.
573 */
574static void rtFsExtExtentIdx_Log(PCEXTEXTENTIDX pExtentIdx)
575{
576 if (LogIs2Enabled())
577 {
578 Log2(("EXT: Extent index node:\n"));
579 Log2(("EXT: iBlock %#RX32\n", RT_LE2H_U32(pExtentIdx->iBlock)));
580 Log2(("EXT: offChildLow %#RX32\n", RT_LE2H_U32(pExtentIdx->offChildLow)));
581 Log2(("EXT: offChildHigh %#RX16\n", RT_LE2H_U16(pExtentIdx->offChildHigh)));
582 }
583}
584
585
586/**
587 * Logs an extent.
588 *
589 * @param pExtent The extent.
590 */
591static void rtFsExtExtent_Log(PCEXTEXTENT pExtent)
592{
593 if (LogIs2Enabled())
594 {
595 Log2(("EXT: Extent:\n"));
596 Log2(("EXT: iBlock %#RX32\n", RT_LE2H_U32(pExtent->iBlock)));
597 Log2(("EXT: cBlocks %#RX16\n", RT_LE2H_U16(pExtent->cBlocks)));
598 Log2(("EXT: offStartHigh %#RX16\n", RT_LE2H_U32(pExtent->offStartHigh)));
599 Log2(("EXT: offStartLow %#RX16\n", RT_LE2H_U16(pExtent->offStartLow)));
600 }
601}
602
603#endif /* LOG_ENABLED */
604
605/**
606 * Converts a block number to a byte offset.
607 *
608 * @returns Offset in bytes for the given block number.
609 * @param pThis The ext volume instance.
610 * @param iBlock The block number to convert.
611 */
612DECLINLINE(uint64_t) rtFsExtBlockIdxToDiskOffset(PRTFSEXTVOL pThis, uint64_t iBlock)
613{
614 return iBlock << pThis->cBlockShift;
615}
616
617
618/**
619 * Converts a byte offset to a block number.
620 *
621 * @returns Block number.
622 * @param pThis The ext volume instance.
623 * @param iBlock The offset to convert.
624 */
625DECLINLINE(uint64_t) rtFsExtDiskOffsetToBlockIdx(PRTFSEXTVOL pThis, uint64_t off)
626{
627 return off >> pThis->cBlockShift;
628}
629
630
631/**
632 * Creates the proper block number from the given low and high parts in case a 64bit
633 * filesystem is used.
634 *
635 * @returns 64bit block number.
636 * @param pThis The ext volume instance.
637 * @param uLow The lower 32bit part.
638 * @param uHigh The upper 32bit part.
639 */
640DECLINLINE(uint64_t) rtFsExtBlockFromLowHigh(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
641{
642 return pThis->f64Bit ? RT_MAKE_U64(uLow, uHigh): uLow;
643}
644
645
646/**
647 * Converts the given high and low parts of the block number to a byte offset.
648 *
649 * @returns Offset in bytes for the given block number.
650 * @param uLow The lower 32bit part of the block number.
651 * @param uHigh The upper 32bit part of the block number.
652 */
653DECLINLINE(uint64_t) rtFsExtBlockIdxLowHighToDiskOffset(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
654{
655 uint64_t iBlock = rtFsExtBlockFromLowHigh(pThis, uLow, uHigh);
656 return rtFsExtBlockIdxToDiskOffset(pThis, iBlock);
657}
658
659
660/**
661 * Allocates a new block group.
662 *
663 * @returns Pointer to the new block group descriptor or NULL if out of memory.
664 * @param pThis The ext volume instance.
665 * @param cbAlloc How much to allocate.
666 * @param iBlockGroup Block group number.
667 */
668static PRTFSEXTBLOCKENTRY rtFsExtVol_BlockAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint64_t iBlock)
669{
670 PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)RTMemAllocZ(cbAlloc);
671 if (RT_LIKELY(pBlock))
672 {
673 pBlock->Core.Key = iBlock;
674 pBlock->cRefs = 0;
675 pThis->cbBlocks += cbAlloc;
676 }
677
678 return pBlock;
679}
680
681
682/**
683 * Returns a new block entry utilizing the cache if possible.
684 *
685 * @returns Pointer to the new block entry or NULL if out of memory.
686 * @param pThis The ext volume instance.
687 * @param iBlock Block number.
688 */
689static PRTFSEXTBLOCKENTRY rtFsExtVol_BlockGetNew(PRTFSEXTVOL pThis, uint64_t iBlock)
690{
691 PRTFSEXTBLOCKENTRY pBlock = NULL;
692 size_t cbAlloc = RT_UOFFSETOF_DYN(RTFSEXTBLOCKENTRY, abData[pThis->cbBlock]);
693 if (pThis->cbBlocks + cbAlloc <= RTFSEXT_MAX_BLOCK_CACHE_SIZE)
694 pBlock = rtFsExtVol_BlockAlloc(pThis, cbAlloc, iBlock);
695 else
696 {
697 pBlock = RTListRemoveLast(&pThis->LstBlockLru, RTFSEXTBLOCKENTRY, NdLru);
698 if (!pBlock)
699 pBlock = rtFsExtVol_BlockAlloc(pThis, cbAlloc, iBlock);
700 else
701 {
702 /* Remove the block group from the tree because it gets a new key. */
703 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
704 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
705 }
706 }
707
708 Assert(!pBlock->cRefs);
709 pBlock->Core.Key = iBlock;
710 pBlock->cRefs = 1;
711
712 return pBlock;
713}
714
715
716/**
717 * Frees the given block.
718 *
719 * @param pThis The ext volume instance.
720 * @param pBlock The block to free.
721 */
722static void rtFsExtVol_BlockFree(PRTFSEXTVOL pThis, PRTFSEXTBLOCKENTRY pBlock)
723{
724 Assert(!pBlock->cRefs);
725
726 /*
727 * Put it into the cache if the limit wasn't exceeded, otherwise the block group
728 * is freed right away.
729 */
730 if (pThis->cbBlocks <= RTFSEXT_MAX_BLOCK_CACHE_SIZE)
731 {
732 /* Put onto the LRU list. */
733 RTListPrepend(&pThis->LstBlockLru, &pBlock->NdLru);
734 }
735 else
736 {
737 /* Remove from the tree and free memory. */
738 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
739 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
740 RTMemFree(pBlock);
741 pThis->cbBlocks -= RT_UOFFSETOF_DYN(RTFSEXTBLOCKENTRY, abData[pThis->cbBlock]);
742 }
743}
744
745
746/**
747 * Gets the specified block data from the volume.
748 *
749 * @returns IPRT status code.
750 * @param pThis The ext volume instance.
751 * @param iBlock The filesystem block to load.
752 * @param ppBlock Where to return the pointer to the block entry on success.
753 * @param ppvData Where to return the pointer to the block data on success.
754 */
755static int rtFsExtVol_BlockLoad(PRTFSEXTVOL pThis, uint64_t iBlock, PRTFSEXTBLOCKENTRY *ppBlock, void **ppvData)
756{
757 int rc = VINF_SUCCESS;
758
759 /* Try to fetch the block group from the cache first. */
760 PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)RTAvlU64Get(&pThis->BlockRoot, iBlock);
761 if (!pBlock)
762 {
763 /* Slow path, load from disk. */
764 pBlock = rtFsExtVol_BlockGetNew(pThis, iBlock);
765 if (RT_LIKELY(pBlock))
766 {
767 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, iBlock);
768 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlock->abData[0], pThis->cbBlock, NULL);
769 if (RT_SUCCESS(rc))
770 {
771 bool fIns = RTAvlU64Insert(&pThis->BlockRoot, &pBlock->Core);
772 Assert(fIns); RT_NOREF(fIns);
773 }
774 }
775 else
776 rc = VERR_NO_MEMORY;
777 }
778 else
779 {
780 /* Remove from current LRU list position and add to the beginning. */
781 uint32_t cRefs = ASMAtomicIncU32(&pBlock->cRefs);
782 if (cRefs == 1) /* Blocks get removed from the LRU list if they are referenced. */
783 RTListNodeRemove(&pBlock->NdLru);
784 }
785
786 if (RT_SUCCESS(rc))
787 {
788 *ppBlock = pBlock;
789 *ppvData = &pBlock->abData[0];
790 }
791 else if (pBlock)
792 {
793 ASMAtomicDecU32(&pBlock->cRefs);
794 rtFsExtVol_BlockFree(pThis, pBlock); /* Free the block. */
795 }
796
797 return rc;
798}
799
800
801/**
802 * Releases a reference of the given block.
803 *
804 * @param pThis The ext volume instance.
805 * @param pBlock The block to release.
806 */
807static void rtFsExtVol_BlockRelease(PRTFSEXTVOL pThis, PRTFSEXTBLOCKENTRY pBlock)
808{
809 uint32_t cRefs = ASMAtomicDecU32(&pBlock->cRefs);
810 if (!cRefs)
811 rtFsExtVol_BlockFree(pThis, pBlock);
812}
813
814
815/**
816 * Allocates a new block group.
817 *
818 * @returns Pointer to the new block group descriptor or NULL if out of memory.
819 * @param pThis The ext volume instance.
820 * @param cbAlloc How much to allocate.
821 * @param iBlockGroup Block group number.
822 */
823static PRTFSEXTBLKGRP rtFsExtBlockGroupAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint32_t iBlockGroup)
824{
825 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTMemAllocZ(cbAlloc);
826 if (RT_LIKELY(pBlockGroup))
827 {
828 pBlockGroup->Core.Key = iBlockGroup;
829 pBlockGroup->cRefs = 0;
830 pBlockGroup->pabInodeBitmap = &pBlockGroup->abBlockBitmap[pThis->cbBlockBitmap];
831 pThis->cbBlockGroups += cbAlloc;
832 }
833
834 return pBlockGroup;
835}
836
837
838/**
839 * Frees the given block group.
840 *
841 * @param pThis The ext volume instance.
842 * @param pBlockGroup The block group to free.
843 */
844static void rtFsExtBlockGroupFree(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
845{
846 Assert(!pBlockGroup->cRefs);
847
848 /*
849 * Put it into the cache if the limit wasn't exceeded, otherwise the block group
850 * is freed right away.
851 */
852 if (pThis->cbBlockGroups <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
853 {
854 /* Put onto the LRU list. */
855 RTListPrepend(&pThis->LstBlockGroupLru, &pBlockGroup->NdLru);
856 }
857 else
858 {
859 /* Remove from the tree and free memory. */
860 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
861 Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
862 RTMemFree(pBlockGroup);
863 pThis->cbBlockGroups -= sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
864 }
865}
866
867
868/**
869 * Returns a new block group utilizing the cache if possible.
870 *
871 * @returns Pointer to the new block group descriptor or NULL if out of memory.
872 * @param pThis The ext volume instance.
873 * @param iBlockGroup Block group number.
874 */
875static PRTFSEXTBLKGRP rtFsExtBlockGroupGetNew(PRTFSEXTVOL pThis, uint32_t iBlockGroup)
876{
877 PRTFSEXTBLKGRP pBlockGroup = NULL;
878 size_t cbAlloc = sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
879 if (pThis->cbBlockGroups + cbAlloc <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
880 pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
881 else
882 {
883 pBlockGroup = RTListRemoveLast(&pThis->LstBlockGroupLru, RTFSEXTBLKGRP, NdLru);
884 if (!pBlockGroup)
885 pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
886 else
887 {
888 /* Remove the block group from the tree because it gets a new key. */
889 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
890 Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
891 }
892 }
893
894 Assert(!pBlockGroup->cRefs);
895 pBlockGroup->Core.Key = iBlockGroup;
896 pBlockGroup->cRefs = 1;
897
898 return pBlockGroup;
899}
900
901
902/**
903 * Loads the given block group number and returns it on success.
904 *
905 * @returns IPRT status code.
906 * @param pThis The ext volume instance.
907 * @param iBlockGroup The block group to load.
908 * @param ppBlockGroup Where to store the block group on success.
909 */
910static int rtFsExtBlockGroupLoad(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PRTFSEXTBLKGRP *ppBlockGroup)
911{
912 int rc = VINF_SUCCESS;
913
914 /* Try to fetch the block group from the cache first. */
915 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTAvlU32Get(&pThis->BlockGroupRoot, iBlockGroup);
916 if (!pBlockGroup)
917 {
918 /* Slow path, load from disk. */
919 pBlockGroup = rtFsExtBlockGroupGetNew(pThis, iBlockGroup);
920 if (RT_LIKELY(pBlockGroup))
921 {
922 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pThis->cbBlock == _1K ? 2 : 1)
923 + (uint64_t)iBlockGroup * pThis->cbBlkGrpDesc;
924 EXTBLOCKGROUPDESC BlockGroupDesc;
925 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &BlockGroupDesc, pThis->cbBlkGrpDesc, NULL);
926 if (RT_SUCCESS(rc))
927 {
928#ifdef LOG_ENABLED
929 rtFsExtBlockGroup_Log(pThis, iBlockGroup, &BlockGroupDesc);
930#endif
931 pBlockGroup->iBlockInodeTbl = RT_LE2H_U32(BlockGroupDesc.v32.offInodeTableLow)
932 | ((pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
933 ? (uint64_t)RT_LE2H_U32(BlockGroupDesc.v64.offInodeTableHigh) << 32
934 : 0);
935
936 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offBlockBitmapLow),
937 RT_LE2H_U32(BlockGroupDesc.v64.offBlockBitmapHigh));
938 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->abBlockBitmap[0], pThis->cbBlockBitmap, NULL);
939 if (RT_SUCCESS(rc))
940 {
941 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offInodeBitmapLow),
942 RT_LE2H_U32(BlockGroupDesc.v64.offInodeBitmapHigh));
943 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->pabInodeBitmap[0], pThis->cbInodeBitmap, NULL);
944 if (RT_SUCCESS(rc))
945 {
946 bool fIns = RTAvlU32Insert(&pThis->BlockGroupRoot, &pBlockGroup->Core);
947 Assert(fIns); RT_NOREF(fIns);
948 }
949 }
950 }
951 }
952 else
953 rc = VERR_NO_MEMORY;
954 }
955 else
956 {
957 /* Remove from current LRU list position and add to the beginning. */
958 uint32_t cRefs = ASMAtomicIncU32(&pBlockGroup->cRefs);
959 if (cRefs == 1) /* Block groups get removed from the LRU list if they are referenced. */
960 RTListNodeRemove(&pBlockGroup->NdLru);
961 }
962
963 if (RT_SUCCESS(rc))
964 *ppBlockGroup = pBlockGroup;
965 else if (pBlockGroup)
966 {
967 ASMAtomicDecU32(&pBlockGroup->cRefs);
968 rtFsExtBlockGroupFree(pThis, pBlockGroup); /* Free the block group. */
969 }
970
971 return rc;
972}
973
974
975/**
976 * Releases a reference of the given block group.
977 *
978 * @param pThis The ext volume instance.
979 * @param pBlockGroup The block group to release.
980 */
981static void rtFsExtBlockGroupRelease(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
982{
983 uint32_t cRefs = ASMAtomicDecU32(&pBlockGroup->cRefs);
984 if (!cRefs)
985 rtFsExtBlockGroupFree(pThis, pBlockGroup);
986}
987
988
989/**
990 * Allocates a new inode.
991 *
992 * @returns Pointer to the new inode or NULL if out of memory.
993 * @param pThis The ext volume instance.
994 * @param iInode Inode number.
995 */
996static PRTFSEXTINODE rtFsExtInodeAlloc(PRTFSEXTVOL pThis, uint32_t iInode)
997{
998 PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTMemAllocZ(sizeof(RTFSEXTINODE));
999 if (RT_LIKELY(pInode))
1000 {
1001 pInode->Core.Key = iInode;
1002 pInode->cRefs = 0;
1003 pThis->cbInodes += sizeof(RTFSEXTINODE);
1004 }
1005
1006 return pInode;
1007}
1008
1009
1010/**
1011 * Frees the given inode.
1012 *
1013 * @param pThis The ext volume instance.
1014 * @param pInode The inode to free.
1015 */
1016static void rtFsExtInodeFree(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
1017{
1018 Assert(!pInode->cRefs);
1019
1020 /*
1021 * Put it into the cache if the limit wasn't exceeded, otherwise the inode
1022 * is freed right away.
1023 */
1024 if (pThis->cbInodes <= RTFSEXT_MAX_INODE_CACHE_SIZE)
1025 {
1026 /* Put onto the LRU list. */
1027 RTListPrepend(&pThis->LstInodeLru, &pInode->NdLru);
1028 }
1029 else
1030 {
1031 /* Remove from the tree and free memory. */
1032 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
1033 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
1034 RTMemFree(pInode);
1035 pThis->cbInodes -= sizeof(RTFSEXTINODE);
1036 }
1037}
1038
1039
1040/**
1041 * Returns a new inodep utilizing the cache if possible.
1042 *
1043 * @returns Pointer to the new inode or NULL if out of memory.
1044 * @param pThis The ext volume instance.
1045 * @param iInode Inode number.
1046 */
1047static PRTFSEXTINODE rtFsExtInodeGetNew(PRTFSEXTVOL pThis, uint32_t iInode)
1048{
1049 PRTFSEXTINODE pInode = NULL;
1050 if (pThis->cbInodes + sizeof(RTFSEXTINODE) <= RTFSEXT_MAX_INODE_CACHE_SIZE)
1051 pInode = rtFsExtInodeAlloc(pThis, iInode);
1052 else
1053 {
1054 pInode = RTListRemoveLast(&pThis->LstInodeLru, RTFSEXTINODE, NdLru);
1055 if (!pInode)
1056 pInode = rtFsExtInodeAlloc(pThis, iInode);
1057 else
1058 {
1059 /* Remove the block group from the tree because it gets a new key. */
1060 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
1061 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
1062 }
1063 }
1064
1065 Assert(!pInode->cRefs);
1066 pInode->Core.Key = iInode;
1067 pInode->cRefs = 1;
1068
1069 return pInode;
1070}
1071
1072
1073/**
1074 * Loads the given inode number and returns it on success.
1075 *
1076 * @returns IPRT status code.
1077 * @param pThis The ext volume instance.
1078 * @param iInode The inode to load.
1079 * @param ppInode Where to store the inode on success.
1080 */
1081static int rtFsExtInodeLoad(PRTFSEXTVOL pThis, uint32_t iInode, PRTFSEXTINODE *ppInode)
1082{
1083 int rc = VINF_SUCCESS;
1084
1085 /* Try to fetch the inode from the cache first. */
1086 PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTAvlU32Get(&pThis->InodeRoot, iInode);
1087 if (!pInode)
1088 {
1089 /* Slow path, load from disk. */
1090 pInode = rtFsExtInodeGetNew(pThis, iInode);
1091 if (RT_LIKELY(pInode))
1092 {
1093 /* Calculate the block group and load that one first to get at the inode table location. */
1094 PRTFSEXTBLKGRP pBlockGroup = NULL;
1095 rc = rtFsExtBlockGroupLoad(pThis, (iInode - 1) / pThis->cInodesPerGroup, &pBlockGroup);
1096 if (RT_SUCCESS(rc))
1097 {
1098 uint32_t idxInodeInTbl = (iInode - 1) % pThis->cInodesPerGroup;
1099 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pBlockGroup->iBlockInodeTbl)
1100 + idxInodeInTbl * pThis->cbInode;
1101
1102 /* Release block group here already as it is not required. */
1103 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
1104
1105 EXTINODECOMB Inode;
1106 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &Inode, RT_MIN(sizeof(Inode), pThis->cbInode), NULL);
1107 if (RT_SUCCESS(rc))
1108 {
1109#ifdef LOG_ENABLED
1110 rtFsExtInode_Log(pThis, iInode, &Inode);
1111#endif
1112 pInode->offInode = offRead;
1113 pInode->fFlags = RT_LE2H_U32(Inode.Core.fFlags);
1114 pInode->ObjInfo.cbObject = (uint64_t)RT_LE2H_U32(Inode.Core.cbSizeHigh) << 32
1115 | (uint64_t)RT_LE2H_U32(Inode.Core.cbSizeLow);
1116 pInode->ObjInfo.cbAllocated = ( (uint64_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.cBlocksHigh) << 32
1117 | (uint64_t)RT_LE2H_U32(Inode.Core.cBlocksLow)) * pThis->cbBlock;
1118 RTTimeSpecSetSeconds(&pInode->ObjInfo.AccessTime, RT_LE2H_U32(Inode.Core.u32TimeLastAccess));
1119 RTTimeSpecSetSeconds(&pInode->ObjInfo.ModificationTime, RT_LE2H_U32(Inode.Core.u32TimeLastModification));
1120 RTTimeSpecSetSeconds(&pInode->ObjInfo.ChangeTime, RT_LE2H_U32(Inode.Core.u32TimeLastChange));
1121 pInode->ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1122 pInode->ObjInfo.Attr.u.Unix.uid = (uint32_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.uUidHigh) << 16
1123 | (uint32_t)RT_LE2H_U16(Inode.Core.uUidLow);
1124 pInode->ObjInfo.Attr.u.Unix.gid = (uint32_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.uGidHigh) << 16
1125 | (uint32_t)RT_LE2H_U16(Inode.Core.uGidLow);
1126 pInode->ObjInfo.Attr.u.Unix.cHardlinks = RT_LE2H_U16(Inode.Core.cHardLinks);
1127 pInode->ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1128 pInode->ObjInfo.Attr.u.Unix.INodeId = iInode;
1129 pInode->ObjInfo.Attr.u.Unix.fFlags = 0;
1130 pInode->ObjInfo.Attr.u.Unix.GenerationId = RT_LE2H_U32(Inode.Core.u32Version);
1131 pInode->ObjInfo.Attr.u.Unix.Device = 0;
1132 if (pThis->cbInode >= sizeof(EXTINODECOMB))
1133 RTTimeSpecSetSeconds(&pInode->ObjInfo.BirthTime, RT_LE2H_U32(Inode.Extra.u32TimeCreation));
1134 else
1135 RTTimeSpecSetSeconds(&pInode->ObjInfo.BirthTime, RT_LE2H_U32(Inode.Core.u32TimeLastChange));
1136 for (unsigned i = 0; i < RT_ELEMENTS(pInode->aiBlocks); i++)
1137 pInode->aiBlocks[i] = RT_LE2H_U32(Inode.Core.au32Block[i]);
1138
1139 /* Fill in the mode. */
1140 pInode->ObjInfo.Attr.fMode = 0;
1141 uint32_t fInodeMode = RT_LE2H_U32(Inode.Core.fMode);
1142 switch (EXT_INODE_MODE_TYPE_GET_TYPE(fInodeMode))
1143 {
1144 case EXT_INODE_MODE_TYPE_FIFO:
1145 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FIFO;
1146 break;
1147 case EXT_INODE_MODE_TYPE_CHAR:
1148 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_CHAR;
1149 break;
1150 case EXT_INODE_MODE_TYPE_DIR:
1151 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DIRECTORY;
1152 break;
1153 case EXT_INODE_MODE_TYPE_BLOCK:
1154 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_BLOCK;
1155 break;
1156 case EXT_INODE_MODE_TYPE_REGULAR:
1157 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FILE;
1158 break;
1159 case EXT_INODE_MODE_TYPE_SYMLINK:
1160 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SYMLINK;
1161 break;
1162 case EXT_INODE_MODE_TYPE_SOCKET:
1163 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SOCKET;
1164 break;
1165 default:
1166 rc = VERR_VFS_BOGUS_FORMAT;
1167 }
1168 if (fInodeMode & EXT_INODE_MODE_EXEC_OTHER)
1169 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXOTH;
1170 if (fInodeMode & EXT_INODE_MODE_WRITE_OTHER)
1171 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWOTH;
1172 if (fInodeMode & EXT_INODE_MODE_READ_OTHER)
1173 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IROTH;
1174 if (fInodeMode & EXT_INODE_MODE_EXEC_GROUP)
1175 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXGRP;
1176 if (fInodeMode & EXT_INODE_MODE_WRITE_GROUP)
1177 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWGRP;
1178 if (fInodeMode & EXT_INODE_MODE_READ_GROUP)
1179 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRGRP;
1180 if (fInodeMode & EXT_INODE_MODE_EXEC_OWNER)
1181 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXUSR;
1182 if (fInodeMode & EXT_INODE_MODE_WRITE_OWNER)
1183 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWUSR;
1184 if (fInodeMode & EXT_INODE_MODE_READ_OWNER)
1185 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRUSR;
1186 if (fInodeMode & EXT_INODE_MODE_STICKY)
1187 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISTXT;
1188 if (fInodeMode & EXT_INODE_MODE_SET_GROUP_ID)
1189 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISGID;
1190 if (fInodeMode & EXT_INODE_MODE_SET_USER_ID)
1191 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISUID;
1192 }
1193 }
1194
1195 if (RT_SUCCESS(rc))
1196 {
1197 bool fIns = RTAvlU32Insert(&pThis->InodeRoot, &pInode->Core);
1198 Assert(fIns); RT_NOREF(fIns);
1199 }
1200 }
1201 else
1202 rc = VERR_NO_MEMORY;
1203 }
1204 else
1205 {
1206 /* Remove from current LRU list position and add to the beginning. */
1207 uint32_t cRefs = ASMAtomicIncU32(&pInode->cRefs);
1208 if (cRefs == 1) /* Inodes get removed from the LRU list if they are referenced. */
1209 RTListNodeRemove(&pInode->NdLru);
1210 }
1211
1212 if (RT_SUCCESS(rc))
1213 *ppInode = pInode;
1214 else if (pInode)
1215 {
1216 ASMAtomicDecU32(&pInode->cRefs);
1217 rtFsExtInodeFree(pThis, pInode); /* Free the inode. */
1218 }
1219
1220 return rc;
1221}
1222
1223
1224/**
1225 * Releases a reference of the given inode.
1226 *
1227 * @param pThis The ext volume instance.
1228 * @param pInode The inode to release.
1229 */
1230static void rtFsExtInodeRelease(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
1231{
1232 uint32_t cRefs = ASMAtomicDecU32(&pInode->cRefs);
1233 if (!cRefs)
1234 rtFsExtInodeFree(pThis, pInode);
1235}
1236
1237
1238/**
1239 * Worker for various QueryInfo methods.
1240 *
1241 * @returns IPRT status code.
1242 * @param pInode The inode structure to return info for.
1243 * @param pObjInfo Where to return object info.
1244 * @param enmAddAttr What additional info to return.
1245 */
1246static int rtFsExtInode_QueryInfo(PRTFSEXTINODE pInode, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1247{
1248 RT_ZERO(*pObjInfo);
1249
1250 pObjInfo->cbObject = pInode->ObjInfo.cbObject;
1251 pObjInfo->cbAllocated = pInode->ObjInfo.cbAllocated;
1252 pObjInfo->AccessTime = pInode->ObjInfo.AccessTime;
1253 pObjInfo->ModificationTime = pInode->ObjInfo.ModificationTime;
1254 pObjInfo->ChangeTime = pInode->ObjInfo.ChangeTime;
1255 pObjInfo->BirthTime = pInode->ObjInfo.BirthTime;
1256 pObjInfo->Attr.fMode = pInode->ObjInfo.Attr.fMode;
1257 pObjInfo->Attr.enmAdditional = enmAddAttr;
1258 switch (enmAddAttr)
1259 {
1260 case RTFSOBJATTRADD_UNIX:
1261 memcpy(&pObjInfo->Attr.u.Unix, &pInode->ObjInfo.Attr.u.Unix, sizeof(pInode->ObjInfo.Attr.u.Unix));
1262 break;
1263
1264 case RTFSOBJATTRADD_UNIX_OWNER:
1265 pObjInfo->Attr.u.UnixOwner.uid = pInode->ObjInfo.Attr.u.Unix.uid;
1266 break;
1267
1268 case RTFSOBJATTRADD_UNIX_GROUP:
1269 pObjInfo->Attr.u.UnixGroup.gid = pInode->ObjInfo.Attr.u.Unix.gid;
1270 break;
1271
1272 default:
1273 break;
1274 }
1275
1276 return VINF_SUCCESS;
1277}
1278
1279
1280/**
1281 * Validates a given extent header.
1282 *
1283 * @returns Flag whether the extent header appears to be valid.
1284 * @param pExtentHdr The extent header to validate.
1285 */
1286DECLINLINE(bool) rtFsExtInode_ExtentHdrValidate(PCEXTEXTENTHDR pExtentHdr)
1287{
1288 return RT_LE2H_U16(pExtentHdr->u16Magic) == EXT_EXTENT_HDR_MAGIC
1289 && RT_LE2H_U16(pExtentHdr->cEntries) <= RT_LE2H_U16(pExtentHdr->cMax)
1290 && RT_LE2H_U16(pExtentHdr->uDepth) <= EXT_EXTENT_HDR_DEPTH_MAX;
1291}
1292
1293
1294/**
1295 * Parses the given extent, checking whether it intersects with the given block.
1296 *
1297 * @returns Flag whether the extent maps the given range (at least partly).
1298 * @param pExtent The extent to parse.
1299 * @param iBlock The starting inode block to map.
1300 * @param cBlocks Number of blocks requested.
1301 * @param piBlockFs Where to store the filesystem block on success.
1302 * @param pcBlocks Where to store the number of contiguous blocks on success.
1303 * @param pfSparse Where to store the sparse flag on success.
1304 */
1305DECLINLINE(bool) rtFsExtInode_ExtentParse(PCEXTEXTENT pExtent, uint64_t iBlock, size_t cBlocks,
1306 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1307{
1308#ifdef LOG_ENABLED
1309 rtFsExtExtent_Log(pExtent);
1310#endif
1311
1312 uint32_t iExtentBlock = RT_LE2H_U32(pExtent->iBlock);
1313 uint16_t cExtentLength = RT_LE2H_U16(pExtent->cBlocks);
1314
1315 /* Length over EXT_EXTENT_LENGTH_LIMIT blocks indicate a sparse extent. */
1316 if (cExtentLength > EXT_EXTENT_LENGTH_LIMIT)
1317 {
1318 *pfSparse = true;
1319 cExtentLength -= EXT_EXTENT_LENGTH_LIMIT;
1320 }
1321 else
1322 *pfSparse = false;
1323
1324 if ( iExtentBlock <= iBlock
1325 && iExtentBlock + cExtentLength > iBlock)
1326 {
1327 uint32_t iBlockRel = iBlock - iExtentBlock;
1328 *pcBlocks = RT_MIN(cBlocks, cExtentLength - iBlockRel);
1329 *piBlockFs = ( ((uint64_t)RT_LE2H_U16(pExtent->offStartHigh)) << 32
1330 | ((uint64_t)RT_LE2H_U32(pExtent->offStartLow))) + iBlockRel;
1331 return true;
1332 }
1333
1334 return false;
1335}
1336
1337
1338/**
1339 * Locates the location of the next level in the extent tree mapping the given block.
1340 *
1341 * @returns Filesystem block number where the next level of the extent is stored.
1342 * @param paExtentIdx Pointer to the array of extent index nodes.
1343 * @param cEntries Number of entries in the extent index node array.
1344 * @param iBlock The block to resolve.
1345 */
1346DECLINLINE(uint64_t) rtFsExtInode_ExtentIndexLocateNextLvl(PCEXTEXTENTIDX paExtentIdx, uint16_t cEntries, uint64_t iBlock)
1347{
1348 for (uint32_t i = 1; i < cEntries; i++)
1349 {
1350 PCEXTEXTENTIDX pPrev = &paExtentIdx[i - 1];
1351 PCEXTEXTENTIDX pCur = &paExtentIdx[i];
1352
1353#ifdef LOG_ENABLED
1354 rtFsExtExtentIdx_Log(pPrev);
1355#endif
1356
1357 if ( RT_LE2H_U32(pPrev->iBlock) <= iBlock
1358 && RT_LE2H_U32(pCur->iBlock) > iBlock)
1359 return (uint64_t)RT_LE2H_U16(pPrev->offChildHigh) << 32
1360 | (uint64_t)RT_LE2H_U32(pPrev->offChildLow);
1361 }
1362
1363 /* Nothing found so far, the blast extent index must cover the block as the array is sorted. */
1364 PCEXTEXTENTIDX pLast = &paExtentIdx[cEntries - 1];
1365#ifdef LOG_ENABLED
1366 rtFsExtExtentIdx_Log(pLast);
1367#endif
1368
1369 return (uint64_t)RT_LE2H_U16(pLast->offChildHigh) << 32
1370 | (uint64_t)RT_LE2H_U32(pLast->offChildLow);
1371}
1372
1373
1374/**
1375 * Maps the given inode block to the destination filesystem block using the embedded extent tree.
1376 *
1377 * @returns IPRT status code.
1378 * @param pThis The ext volume instance.
1379 * @param pInode The inode structure to read from.
1380 * @param iBlock The starting inode block to map.
1381 * @param cBlocks Number of blocks requested.
1382 * @param piBlockFs Where to store the filesystem block on success.
1383 * @param pcBlocks Where to store the number of contiguous blocks on success.
1384 * @param pfSparse Where to store the sparse flag on success.
1385 *
1386 * @todo Optimize
1387 */
1388static int rtFsExtInode_MapBlockToFsViaExtent(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1389 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1390{
1391 int rc = VINF_SUCCESS;
1392
1393 /* The root of the extent tree is located in the block data of the inode. */
1394 PCEXTEXTENTHDR pExtentHdr = (PCEXTEXTENTHDR)&pInode->aiBlocks[0];
1395
1396#ifdef LOG_ENABLED
1397 rtFsExtExtentHdr_Log(pExtentHdr);
1398#endif
1399
1400 /*
1401 * Some validation, the top level is located inside the inode block data
1402 * and has a maxmimum of 4 entries.
1403 */
1404 if ( rtFsExtInode_ExtentHdrValidate(pExtentHdr)
1405 && RT_LE2H_U16(pExtentHdr->cMax) <= 4)
1406 {
1407 uint16_t uDepthCur = RT_LE2H_U16(pExtentHdr->uDepth);
1408 if (!uDepthCur)
1409 {
1410 PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
1411
1412 rc = VERR_VFS_BOGUS_FORMAT;
1413 for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
1414 {
1415 /* Check whether the extent intersects with the block. */
1416 if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
1417 {
1418 rc = VINF_SUCCESS;
1419 break;
1420 }
1421 pExtent++;
1422 }
1423 }
1424 else
1425 {
1426 uint8_t *pbExtent = NULL;
1427 PRTFSEXTBLOCKENTRY pBlock = NULL;
1428 uint64_t iBlockNext = 0;
1429 PCEXTEXTENTIDX paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
1430 uint16_t cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
1431
1432 /* Descend the tree until we reached the leaf nodes. */
1433 do
1434 {
1435 iBlockNext = rtFsExtInode_ExtentIndexLocateNextLvl(paExtentIdx, cEntries, iBlock);
1436 /* Read in the full block. */
1437 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&pbExtent);
1438 if (RT_SUCCESS(rc))
1439 {
1440 pExtentHdr = (PCEXTEXTENTHDR)pbExtent;
1441
1442#ifdef LOG_ENABLED
1443 rtFsExtExtentHdr_Log(pExtentHdr);
1444#endif
1445
1446 if ( rtFsExtInode_ExtentHdrValidate(pExtentHdr)
1447 && RT_LE2H_U16(pExtentHdr->cMax) <= (pThis->cbBlock - sizeof(EXTEXTENTHDR)) / sizeof(EXTEXTENTIDX)
1448 && RT_LE2H_U16(pExtentHdr->uDepth) == uDepthCur - 1)
1449 {
1450 uDepthCur--;
1451 cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
1452 paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
1453 if (uDepthCur)
1454 rtFsExtVol_BlockRelease(pThis, pBlock);
1455 }
1456 else
1457 rc = VERR_VFS_BOGUS_FORMAT;
1458 }
1459 }
1460 while ( uDepthCur > 0
1461 && RT_SUCCESS(rc));
1462
1463 if (RT_SUCCESS(rc))
1464 {
1465 Assert(!uDepthCur);
1466
1467 /* We reached the leaf nodes. */
1468 PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
1469 for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
1470 {
1471 /* Check whether the extent intersects with the block. */
1472 if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
1473 {
1474 rc = VINF_SUCCESS;
1475 break;
1476 }
1477 pExtent++;
1478 }
1479 }
1480
1481 if (pBlock)
1482 rtFsExtVol_BlockRelease(pThis, pBlock);
1483 }
1484 }
1485 else
1486 rc = VERR_VFS_BOGUS_FORMAT;
1487
1488 return rc;
1489}
1490
1491
1492/**
1493 * Maps the given inode block to the destination filesystem block using the original block mapping scheme.
1494 *
1495 * @returns IPRT status code.
1496 * @param pThis The ext volume instance.
1497 * @param pInode The inode structure to read from.
1498 * @param iBlock The inode block to map.
1499 * @param cBlocks Number of blocks requested.
1500 * @param piBlockFs Where to store the filesystem block on success.
1501 * @param pcBlocks Where to store the number of contiguous blocks on success.
1502 * @param pfSparse Where to store the sparse flag on success.
1503 *
1504 * @todo Optimize and handle sparse files.
1505 */
1506static int rtFsExtInode_MapBlockToFsViaBlockMap(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1507 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1508{
1509 int rc = VINF_SUCCESS;
1510 RT_NOREF(cBlocks);
1511
1512 *pfSparse = false;
1513 *pcBlocks = 1;
1514
1515 /* The first 12 inode blocks are directly mapped from the inode. */
1516 if (iBlock <= 11)
1517 *piBlockFs = pInode->aiBlocks[iBlock];
1518 else
1519 {
1520 uint32_t cEntriesPerBlockMap = (uint32_t)(pThis->cbBlock >> sizeof(uint32_t));
1521
1522 if (iBlock <= cEntriesPerBlockMap + 11)
1523 {
1524 /* Indirect block. */
1525 PRTFSEXTBLOCKENTRY pBlock = NULL;
1526 uint32_t *paBlockMap = NULL;
1527 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[12], &pBlock, (void **)&paBlockMap);
1528 if (RT_SUCCESS(rc))
1529 {
1530 *piBlockFs = RT_LE2H_U32(paBlockMap[iBlock - 12]);
1531 rtFsExtVol_BlockRelease(pThis, pBlock);
1532 }
1533 }
1534 else if (iBlock <= cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap + 11)
1535 {
1536 /* Double indirect block. */
1537 PRTFSEXTBLOCKENTRY pBlock = NULL;
1538 uint32_t *paBlockMap = NULL;
1539
1540 iBlock -= 12 + cEntriesPerBlockMap;
1541 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[13], &pBlock, (void **)&paBlockMap);
1542 if (RT_SUCCESS(rc))
1543 {
1544 uint32_t idxBlockL2 = iBlock / cEntriesPerBlockMap;
1545 uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
1546 uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
1547
1548 rtFsExtVol_BlockRelease(pThis, pBlock);
1549 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1550 if (RT_SUCCESS(rc))
1551 {
1552 *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
1553 rtFsExtVol_BlockRelease(pThis, pBlock);
1554 }
1555 }
1556 }
1557 else
1558 {
1559 /* Triple indirect block. */
1560 PRTFSEXTBLOCKENTRY pBlock = NULL;
1561 uint32_t *paBlockMap = NULL;
1562
1563 iBlock -= 12 + cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap;
1564 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[14], &pBlock, (void **)&paBlockMap);
1565 if (RT_SUCCESS(rc))
1566 {
1567 uint32_t idxBlockL3 = iBlock / (cEntriesPerBlockMap * cEntriesPerBlockMap);
1568 uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL3]);
1569
1570 rtFsExtVol_BlockRelease(pThis, pBlock);
1571 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1572 if (RT_SUCCESS(rc))
1573 {
1574 uint32_t idxBlockL2 = (iBlock % (cEntriesPerBlockMap * cEntriesPerBlockMap)) / cEntriesPerBlockMap;
1575 uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
1576 iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
1577
1578 rtFsExtVol_BlockRelease(pThis, pBlock);
1579 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1580 if (RT_SUCCESS(rc))
1581 {
1582 *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
1583 rtFsExtVol_BlockRelease(pThis, pBlock);
1584 }
1585 }
1586 }
1587 }
1588 }
1589
1590 return rc;
1591}
1592
1593
1594/**
1595 * Maps the given inode block to the destination filesystem block.
1596 *
1597 * @returns IPRT status code.
1598 * @param pThis The ext volume instance.
1599 * @param pInode The inode structure to read from.
1600 * @param iBlock The inode block to map.
1601 * @param cBlocks Number of blocks requested.
1602 * @param piBlockFs Where to store the filesystem block on success.
1603 * @param pcBlocks Where to store the number of contiguous blocks on success.
1604 * @param pfSparse Where to store the sparse flag on success.
1605 *
1606 * @todo Optimize
1607 */
1608static int rtFsExtInode_MapBlockToFs(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1609 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1610{
1611 if (pInode->fFlags & EXT_INODE_F_EXTENTS)
1612 return rtFsExtInode_MapBlockToFsViaExtent(pThis, pInode, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse);
1613 else
1614 return rtFsExtInode_MapBlockToFsViaBlockMap(pThis, pInode, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse);
1615}
1616
1617
1618/**
1619 * Reads data from the given inode at the given byte offset.
1620 *
1621 * @returns IPRT status code.
1622 * @param pThis The ext volume instance.
1623 * @param pInode The inode structure to read from.
1624 * @param off The byte offset to start reading from.
1625 * @param pvBuf Where to store the read data to.
1626 * @param pcbRead Where to return the amount of data read.
1627 */
1628static int rtFsExtInode_Read(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t off, void *pvBuf, size_t cbRead, size_t *pcbRead)
1629{
1630 int rc = VINF_SUCCESS;
1631 uint8_t *pbBuf = (uint8_t *)pvBuf;
1632
1633 if (((uint64_t)pInode->ObjInfo.cbObject < off + cbRead))
1634 {
1635 if (!pcbRead)
1636 return VERR_EOF;
1637 else
1638 cbRead = (uint64_t)pInode->ObjInfo.cbObject - off;
1639 }
1640
1641 while ( cbRead
1642 && RT_SUCCESS(rc))
1643 {
1644 uint64_t iBlockStart = rtFsExtDiskOffsetToBlockIdx(pThis, off);
1645 uint32_t offBlockStart = off % pThis->cbBlock;
1646
1647 /* Resolve the inode block to the proper filesystem block. */
1648 uint64_t iBlockFs = 0;
1649 size_t cBlocks = 0;
1650 bool fSparse = false;
1651 rc = rtFsExtInode_MapBlockToFs(pThis, pInode, iBlockStart, 1, &iBlockFs, &cBlocks, &fSparse);
1652 if (RT_SUCCESS(rc))
1653 {
1654 Assert(cBlocks == 1);
1655
1656 size_t cbThisRead = RT_MIN(cbRead, pThis->cbBlock - offBlockStart);
1657
1658 if (!fSparse)
1659 {
1660 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, iBlockFs);
1661 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead + offBlockStart, pbBuf, cbThisRead, NULL);
1662 }
1663 else
1664 memset(pbBuf, 0, cbThisRead);
1665
1666 if (RT_SUCCESS(rc))
1667 {
1668 pbBuf += cbThisRead;
1669 cbRead -= cbThisRead;
1670 off += cbThisRead;
1671 if (pcbRead)
1672 *pcbRead += cbThisRead;
1673 }
1674 }
1675 }
1676
1677 return rc;
1678}
1679
1680
1681
1682/*
1683 *
1684 * File operations.
1685 * File operations.
1686 * File operations.
1687 *
1688 */
1689
1690/**
1691 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1692 */
1693static DECLCALLBACK(int) rtFsExtFile_Close(void *pvThis)
1694{
1695 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1696 LogFlow(("rtFsExtFile_Close(%p/%p)\n", pThis, pThis->pInode));
1697
1698 rtFsExtInodeRelease(pThis->pVol, pThis->pInode);
1699 pThis->pInode = NULL;
1700 pThis->pVol = NULL;
1701 return VINF_SUCCESS;
1702}
1703
1704
1705/**
1706 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1707 */
1708static DECLCALLBACK(int) rtFsExtFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1709{
1710 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1711 return rtFsExtInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
1712}
1713
1714
1715/**
1716 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1717 */
1718static DECLCALLBACK(int) rtFsExtFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1719{
1720 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1721 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
1722 RT_NOREF(fBlocking);
1723
1724 if (off == -1)
1725 off = pThis->offFile;
1726 else
1727 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
1728
1729 int rc;
1730 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
1731 if (!pcbRead)
1732 {
1733 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1734 if (RT_SUCCESS(rc))
1735 pThis->offFile = off + cbRead;
1736 Log6(("rtFsExtFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
1737 }
1738 else
1739 {
1740 PRTFSEXTINODE pInode = pThis->pInode;
1741 if (off >= pInode->ObjInfo.cbObject)
1742 {
1743 *pcbRead = 0;
1744 rc = VINF_EOF;
1745 }
1746 else
1747 {
1748 if ((uint64_t)off + cbRead <= (uint64_t)pInode->ObjInfo.cbObject)
1749 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1750 else
1751 {
1752 /* Return VINF_EOF if beyond end-of-file. */
1753 cbRead = (size_t)(pInode->ObjInfo.cbObject - off);
1754 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1755 if (RT_SUCCESS(rc))
1756 rc = VINF_EOF;
1757 }
1758 if (RT_SUCCESS(rc))
1759 {
1760 pThis->offFile = off + cbRead;
1761 *pcbRead = cbRead;
1762 }
1763 else
1764 *pcbRead = 0;
1765 }
1766 Log6(("rtFsExtFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
1767 }
1768
1769 return rc;
1770}
1771
1772
1773/**
1774 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1775 */
1776static DECLCALLBACK(int) rtFsExtFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1777{
1778 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
1779 return VERR_WRITE_PROTECT;
1780}
1781
1782
1783/**
1784 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1785 */
1786static DECLCALLBACK(int) rtFsExtFile_Flush(void *pvThis)
1787{
1788 RT_NOREF(pvThis);
1789 return VINF_SUCCESS;
1790}
1791
1792
1793/**
1794 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1795 */
1796static DECLCALLBACK(int) rtFsExtFile_Tell(void *pvThis, PRTFOFF poffActual)
1797{
1798 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1799 *poffActual = pThis->offFile;
1800 return VINF_SUCCESS;
1801}
1802
1803
1804/**
1805 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1806 */
1807static DECLCALLBACK(int) rtFsExtFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1808{
1809 RT_NOREF(pvThis, fMode, fMask);
1810 return VERR_WRITE_PROTECT;
1811}
1812
1813
1814/**
1815 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1816 */
1817static DECLCALLBACK(int) rtFsExtFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1818 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1819{
1820 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1821 return VERR_WRITE_PROTECT;
1822}
1823
1824
1825/**
1826 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1827 */
1828static DECLCALLBACK(int) rtFsExtFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1829{
1830 RT_NOREF(pvThis, uid, gid);
1831 return VERR_WRITE_PROTECT;
1832}
1833
1834
1835/**
1836 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1837 */
1838static DECLCALLBACK(int) rtFsExtFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1839{
1840 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1841 RTFOFF offNew;
1842 switch (uMethod)
1843 {
1844 case RTFILE_SEEK_BEGIN:
1845 offNew = offSeek;
1846 break;
1847 case RTFILE_SEEK_END:
1848 offNew = pThis->pInode->ObjInfo.cbObject + offSeek;
1849 break;
1850 case RTFILE_SEEK_CURRENT:
1851 offNew = (RTFOFF)pThis->offFile + offSeek;
1852 break;
1853 default:
1854 return VERR_INVALID_PARAMETER;
1855 }
1856 if (offNew >= 0)
1857 {
1858 pThis->offFile = offNew;
1859 *poffActual = offNew;
1860 return VINF_SUCCESS;
1861 }
1862 return VERR_NEGATIVE_SEEK;
1863}
1864
1865
1866/**
1867 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1868 */
1869static DECLCALLBACK(int) rtFsExtFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1870{
1871 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1872 *pcbFile = (uint64_t)pThis->pInode->ObjInfo.cbObject;
1873 return VINF_SUCCESS;
1874}
1875
1876
1877/**
1878 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
1879 */
1880static DECLCALLBACK(int) rtFsExtFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
1881{
1882 RT_NOREF(pvThis, cbFile, fFlags);
1883 return VERR_WRITE_PROTECT;
1884}
1885
1886
1887/**
1888 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
1889 */
1890static DECLCALLBACK(int) rtFsExtFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
1891{
1892 RT_NOREF(pvThis);
1893 *pcbMax = INT64_MAX; /** @todo */
1894 return VINF_SUCCESS;
1895}
1896
1897
1898/**
1899 * EXT file operations.
1900 */
1901static const RTVFSFILEOPS g_rtFsExtFileOps =
1902{
1903 { /* Stream */
1904 { /* Obj */
1905 RTVFSOBJOPS_VERSION,
1906 RTVFSOBJTYPE_FILE,
1907 "EXT File",
1908 rtFsExtFile_Close,
1909 rtFsExtFile_QueryInfo,
1910 NULL,
1911 RTVFSOBJOPS_VERSION
1912 },
1913 RTVFSIOSTREAMOPS_VERSION,
1914 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1915 rtFsExtFile_Read,
1916 rtFsExtFile_Write,
1917 rtFsExtFile_Flush,
1918 NULL /*PollOne*/,
1919 rtFsExtFile_Tell,
1920 NULL /*pfnSkip*/,
1921 NULL /*pfnZeroFill*/,
1922 RTVFSIOSTREAMOPS_VERSION,
1923 },
1924 RTVFSFILEOPS_VERSION,
1925 0,
1926 { /* ObjSet */
1927 RTVFSOBJSETOPS_VERSION,
1928 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1929 rtFsExtFile_SetMode,
1930 rtFsExtFile_SetTimes,
1931 rtFsExtFile_SetOwner,
1932 RTVFSOBJSETOPS_VERSION
1933 },
1934 rtFsExtFile_Seek,
1935 rtFsExtFile_QuerySize,
1936 rtFsExtFile_SetSize,
1937 rtFsExtFile_QueryMaxSize,
1938 RTVFSFILEOPS_VERSION
1939};
1940
1941
1942/**
1943 * Creates a new VFS file from the given regular file inode.
1944 *
1945 * @returns IPRT status code.
1946 * @param pThis The ext volume instance.
1947 * @param fOpen Open flags passed.
1948 * @param iInode The inode for the file.
1949 * @param phVfsFile Where to store the VFS file handle on success.
1950 * @param pErrInfo Where to record additional error information on error, optional.
1951 * @param pszWhat Logging prefix.
1952 */
1953static int rtFsExtVol_NewFile(PRTFSEXTVOL pThis, uint64_t fOpen, uint32_t iInode,
1954 PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo, const char *pszWhat)
1955{
1956 /*
1957 * Load the inode and check that it really is a file.
1958 */
1959 PRTFSEXTINODE pInode = NULL;
1960 int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
1961 if (RT_SUCCESS(rc))
1962 {
1963 if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
1964 {
1965 PRTFSEXTFILE pNewFile;
1966 rc = RTVfsNewFile(&g_rtFsExtFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
1967 phVfsFile, (void **)&pNewFile);
1968 if (RT_SUCCESS(rc))
1969 {
1970 pNewFile->pVol = pThis;
1971 pNewFile->pInode = pInode;
1972 pNewFile->offFile = 0;
1973 }
1974 }
1975 else
1976 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: fMode=%#RX32", pszWhat, pInode->ObjInfo.Attr.fMode);
1977
1978 if (RT_FAILURE(rc))
1979 rtFsExtInodeRelease(pThis, pInode);
1980 }
1981
1982 return rc;
1983}
1984
1985
1986
1987/*
1988 *
1989 * EXT directory code.
1990 * EXT directory code.
1991 * EXT directory code.
1992 *
1993 */
1994
1995/**
1996 * Looks up an entry in the given directory inode.
1997 *
1998 * @returns IPRT status code.
1999 * @param pThis The ext volume instance.
2000 * @param pInode The directory inode structure to.
2001 * @param pszEntry The entry to lookup.
2002 * @param piInode Where to store the inode number if the entry was found.
2003 */
2004static int rtFsExtDir_Lookup(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, const char *pszEntry, uint32_t *piInode)
2005{
2006 uint64_t offEntry = 0;
2007 int rc = VERR_FILE_NOT_FOUND;
2008 uint32_t idxDirEntry = 0;
2009 size_t cchEntry = strlen(pszEntry);
2010
2011 if (cchEntry > 255)
2012 return VERR_FILENAME_TOO_LONG;
2013
2014 while (offEntry < (uint64_t)pInode->ObjInfo.cbObject)
2015 {
2016 EXTDIRENTRYEX DirEntry;
2017 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - offEntry);
2018 int rc2 = rtFsExtInode_Read(pThis, pInode, offEntry, &DirEntry, cbThis, NULL);
2019 if (RT_SUCCESS(rc2))
2020 {
2021#ifdef LOG_ENABLED
2022 rtFsExtDirEntry_Log(pThis, idxDirEntry, &DirEntry);
2023#endif
2024
2025 uint16_t cbName = pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
2026 ? DirEntry.Core.u.v2.cbName
2027 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
2028 if ( cchEntry == cbName
2029 && !memcmp(pszEntry, &DirEntry.Core.achName[0], cchEntry))
2030 {
2031 *piInode = RT_LE2H_U32(DirEntry.Core.iInodeRef);
2032 rc = VINF_SUCCESS;
2033 break;
2034 }
2035
2036 offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
2037 idxDirEntry++;
2038 }
2039 else
2040 {
2041 rc = rc2;
2042 break;
2043 }
2044 }
2045
2046 return rc;
2047}
2048
2049
2050
2051/*
2052 *
2053 * Directory instance methods
2054 * Directory instance methods
2055 * Directory instance methods
2056 *
2057 */
2058
2059/**
2060 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2061 */
2062static DECLCALLBACK(int) rtFsExtDir_Close(void *pvThis)
2063{
2064 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2065 LogFlowFunc(("pThis=%p\n", pThis));
2066 rtFsExtInodeRelease(pThis->pVol, pThis->pInode);
2067 pThis->pInode = NULL;
2068 return VINF_SUCCESS;
2069}
2070
2071
2072/**
2073 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2074 */
2075static DECLCALLBACK(int) rtFsExtDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2076{
2077 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2078 LogFlowFunc(("\n"));
2079 return rtFsExtInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
2080}
2081
2082
2083/**
2084 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2085 */
2086static DECLCALLBACK(int) rtFsExtDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2087{
2088 LogFlowFunc(("\n"));
2089 RT_NOREF(pvThis, fMode, fMask);
2090 return VERR_WRITE_PROTECT;
2091}
2092
2093
2094/**
2095 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2096 */
2097static DECLCALLBACK(int) rtFsExtDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2098 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2099{
2100 LogFlowFunc(("\n"));
2101 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2102 return VERR_WRITE_PROTECT;
2103}
2104
2105
2106/**
2107 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2108 */
2109static DECLCALLBACK(int) rtFsExtDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2110{
2111 LogFlowFunc(("\n"));
2112 RT_NOREF(pvThis, uid, gid);
2113 return VERR_WRITE_PROTECT;
2114}
2115
2116
2117/**
2118 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
2119 */
2120static DECLCALLBACK(int) rtFsExtDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
2121 uint32_t fFlags, PRTVFSOBJ phVfsObj)
2122{
2123 LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
2124 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2125 PRTFSEXTVOL pVol = pThis->pVol;
2126 int rc = VINF_SUCCESS;
2127
2128 RT_NOREF(fFlags);
2129
2130 /*
2131 * We cannot create or replace anything, just open stuff.
2132 */
2133 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
2134 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
2135 { /* likely */ }
2136 else
2137 return VERR_WRITE_PROTECT;
2138
2139 /*
2140 * Lookup the entry.
2141 */
2142 uint32_t iInode = 0;
2143 rc = rtFsExtDir_Lookup(pVol, pThis->pInode, pszEntry, &iInode);
2144 if (RT_SUCCESS(rc))
2145 {
2146 PRTFSEXTINODE pInode = NULL;
2147 rc = rtFsExtInodeLoad(pVol, iInode, &pInode);
2148 if (RT_SUCCESS(rc))
2149 {
2150 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
2151 {
2152 RTVFSDIR hVfsDir;
2153 rc = rtFsExtVol_OpenDirByInode(pVol, iInode, &hVfsDir);
2154 if (RT_SUCCESS(rc))
2155 {
2156 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2157 RTVfsDirRelease(hVfsDir);
2158 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2159 }
2160 }
2161 else if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
2162 {
2163 RTVFSFILE hVfsFile;
2164 rc = rtFsExtVol_NewFile(pVol, fOpen, iInode, &hVfsFile, NULL, pszEntry);
2165 if (RT_SUCCESS(rc))
2166 {
2167 *phVfsObj = RTVfsObjFromFile(hVfsFile);
2168 RTVfsFileRelease(hVfsFile);
2169 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2170 }
2171 }
2172 else
2173 rc = VERR_NOT_SUPPORTED;
2174 }
2175 }
2176
2177 LogFlow(("rtFsExtDir_Open(%s): returns %Rrc\n", pszEntry, rc));
2178 return rc;
2179}
2180
2181
2182/**
2183 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
2184 */
2185static DECLCALLBACK(int) rtFsExtDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
2186{
2187 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
2188 LogFlowFunc(("\n"));
2189 return VERR_WRITE_PROTECT;
2190}
2191
2192
2193/**
2194 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
2195 */
2196static DECLCALLBACK(int) rtFsExtDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
2197{
2198 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
2199 LogFlowFunc(("\n"));
2200 return VERR_NOT_SUPPORTED;
2201}
2202
2203
2204/**
2205 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
2206 */
2207static DECLCALLBACK(int) rtFsExtDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
2208 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
2209{
2210 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
2211 LogFlowFunc(("\n"));
2212 return VERR_WRITE_PROTECT;
2213}
2214
2215
2216/**
2217 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
2218 */
2219static DECLCALLBACK(int) rtFsExtDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
2220{
2221 RT_NOREF(pvThis, pszEntry, fType);
2222 LogFlowFunc(("\n"));
2223 return VERR_WRITE_PROTECT;
2224}
2225
2226
2227/**
2228 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
2229 */
2230static DECLCALLBACK(int) rtFsExtDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
2231{
2232 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
2233 LogFlowFunc(("\n"));
2234 return VERR_WRITE_PROTECT;
2235}
2236
2237
2238/**
2239 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
2240 */
2241static DECLCALLBACK(int) rtFsExtDir_RewindDir(void *pvThis)
2242{
2243 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2244 LogFlowFunc(("\n"));
2245
2246 pThis->fNoMoreFiles = false;
2247 pThis->offEntry = 0;
2248 pThis->idxEntry = 0;
2249 return VINF_SUCCESS;
2250}
2251
2252
2253/**
2254 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
2255 */
2256static DECLCALLBACK(int) rtFsExtDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
2257 RTFSOBJATTRADD enmAddAttr)
2258{
2259 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2260 PRTFSEXTINODE pInode = pThis->pInode;
2261 LogFlowFunc(("\n"));
2262
2263 if (pThis->fNoMoreFiles)
2264 return VERR_NO_MORE_FILES;
2265
2266 EXTDIRENTRYEX DirEntry;
2267 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - pThis->offEntry);
2268 int rc = rtFsExtInode_Read(pThis->pVol, pInode, pThis->offEntry, &DirEntry, cbThis, NULL);
2269 if (RT_SUCCESS(rc))
2270 {
2271#ifdef LOG_ENABLED
2272 rtFsExtDirEntry_Log(pThis->pVol, pThis->idxEntry, &DirEntry);
2273#endif
2274
2275 /* 0 inode entry means unused entry. */
2276 /** @todo Can there be unused entries somewhere in the middle? */
2277 uint32_t iInodeRef = RT_LE2H_U32(DirEntry.Core.iInodeRef);
2278 if (iInodeRef != 0)
2279 {
2280 uint16_t cbName = pThis->pVol->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
2281 ? DirEntry.Core.u.v2.cbName
2282 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
2283
2284 if (cbName <= 255)
2285 {
2286 size_t const cbDirEntry = *pcbDirEntry;
2287
2288 *pcbDirEntry = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cbName + 2]);
2289 if (*pcbDirEntry <= cbDirEntry)
2290 {
2291 /* Load the referenced inode. */
2292 PRTFSEXTINODE pInodeRef;
2293 rc = rtFsExtInodeLoad(pThis->pVol, iInodeRef, &pInodeRef);
2294 if (RT_SUCCESS(rc))
2295 {
2296 memcpy(&pDirEntry->szName[0], &DirEntry.Core.achName[0], cbName);
2297 pDirEntry->szName[cbName] = '\0';
2298 pDirEntry->cbName = cbName;
2299 rc = rtFsExtInode_QueryInfo(pInodeRef, &pDirEntry->Info, enmAddAttr);
2300 if (RT_SUCCESS(rc))
2301 {
2302 pThis->offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
2303 pThis->idxEntry++;
2304 rtFsExtInodeRelease(pThis->pVol, pInodeRef);
2305 return VINF_SUCCESS;
2306 }
2307 rtFsExtInodeRelease(pThis->pVol, pInodeRef);
2308 }
2309 }
2310 else
2311 rc = VERR_BUFFER_OVERFLOW;
2312 }
2313 else
2314 rc = VERR_FILENAME_TOO_LONG;
2315 }
2316 else
2317 {
2318 rc = VERR_NO_MORE_FILES;
2319 LogFlowFunc(("no more files\n"));
2320 pThis->fNoMoreFiles = true;
2321 }
2322 }
2323
2324 return rc;
2325}
2326
2327
2328/**
2329 * EXT directory operations.
2330 */
2331static const RTVFSDIROPS g_rtFsExtDirOps =
2332{
2333 { /* Obj */
2334 RTVFSOBJOPS_VERSION,
2335 RTVFSOBJTYPE_DIR,
2336 "EXT Dir",
2337 rtFsExtDir_Close,
2338 rtFsExtDir_QueryInfo,
2339 NULL,
2340 RTVFSOBJOPS_VERSION
2341 },
2342 RTVFSDIROPS_VERSION,
2343 0,
2344 { /* ObjSet */
2345 RTVFSOBJSETOPS_VERSION,
2346 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
2347 rtFsExtDir_SetMode,
2348 rtFsExtDir_SetTimes,
2349 rtFsExtDir_SetOwner,
2350 RTVFSOBJSETOPS_VERSION
2351 },
2352 rtFsExtDir_Open,
2353 NULL /* pfnFollowAbsoluteSymlink */,
2354 NULL /* pfnOpenFile */,
2355 NULL /* pfnOpenDir */,
2356 rtFsExtDir_CreateDir,
2357 rtFsExtDir_OpenSymlink,
2358 rtFsExtDir_CreateSymlink,
2359 NULL /* pfnQueryEntryInfo */,
2360 rtFsExtDir_UnlinkEntry,
2361 rtFsExtDir_RenameEntry,
2362 rtFsExtDir_RewindDir,
2363 rtFsExtDir_ReadDir,
2364 RTVFSDIROPS_VERSION,
2365};
2366
2367
2368/**
2369 * Opens a directory by the given inode.
2370 *
2371 * @returns IPRT status code.
2372 * @param pThis The ext volume instance.
2373 * @param iInode The inode to open.
2374 * @param phVfsDir Where to store the handle to the VFS directory on success.
2375 */
2376static int rtFsExtVol_OpenDirByInode(PRTFSEXTVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir)
2377{
2378 PRTFSEXTINODE pInode = NULL;
2379 int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
2380 if (RT_SUCCESS(rc))
2381 {
2382 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
2383 {
2384 PRTFSEXTDIR pNewDir;
2385 rc = RTVfsNewDir(&g_rtFsExtDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
2386 phVfsDir, (void **)&pNewDir);
2387 if (RT_SUCCESS(rc))
2388 {
2389 pNewDir->fNoMoreFiles = false;
2390 pNewDir->pVol = pThis;
2391 pNewDir->pInode = pInode;
2392 }
2393 }
2394 else
2395 rc = VERR_VFS_BOGUS_FORMAT;
2396
2397 if (RT_FAILURE(rc))
2398 rtFsExtInodeRelease(pThis, pInode);
2399 }
2400
2401 return rc;
2402}
2403
2404
2405
2406/*
2407 *
2408 * Volume level code.
2409 * Volume level code.
2410 * Volume level code.
2411 *
2412 */
2413
2414/**
2415 * Checks whether the block range in the given block group is in use by checking the
2416 * block bitmap.
2417 *
2418 * @returns Flag whether the range is in use.
2419 * @param pBlkGrpDesc The block group to check for.
2420 * @param iBlockStart The starting block to check relative from the beginning of the block group.
2421 * @param cBlocks How many blocks to check.
2422 */
2423static bool rtFsExtIsBlockRangeInUse(PRTFSEXTBLKGRP pBlkGrpDesc, uint64_t iBlockStart, size_t cBlocks)
2424{
2425 /** @todo Optimize with ASMBitFirstSet(). */
2426 while (cBlocks)
2427 {
2428 uint32_t idxByte = iBlockStart / 8;
2429 uint32_t iBit = iBlockStart % 8;
2430
2431 if (pBlkGrpDesc->abBlockBitmap[idxByte] & RT_BIT(iBit))
2432 return true;
2433
2434 cBlocks--;
2435 iBlockStart++;
2436 }
2437
2438 return false;
2439}
2440
2441
2442static DECLCALLBACK(int) rtFsExtVolBlockGroupTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
2443{
2444 RT_NOREF(pvUser);
2445
2446 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)pCore;
2447 Assert(!pBlockGroup->cRefs);
2448 RTMemFree(pBlockGroup);
2449 return VINF_SUCCESS;
2450}
2451
2452
2453static DECLCALLBACK(int) rtFsExtVolInodeTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
2454{
2455 RT_NOREF(pvUser);
2456
2457 PRTFSEXTINODE pInode = (PRTFSEXTINODE)pCore;
2458 Assert(!pInode->cRefs);
2459 RTMemFree(pInode);
2460 return VINF_SUCCESS;
2461}
2462
2463
2464static DECLCALLBACK(int) rtFsExtVolBlockTreeDestroy(PAVLU64NODECORE pCore, void *pvUser)
2465{
2466 RT_NOREF(pvUser);
2467
2468 PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)pCore;
2469 Assert(!pBlock->cRefs);
2470 RTMemFree(pBlock);
2471 return VINF_SUCCESS;
2472}
2473
2474
2475/**
2476 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
2477 */
2478static DECLCALLBACK(int) rtFsExtVol_Close(void *pvThis)
2479{
2480 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2481
2482 /* Destroy the block group tree. */
2483 RTAvlU32Destroy(&pThis->BlockGroupRoot, rtFsExtVolBlockGroupTreeDestroy, pThis);
2484 pThis->BlockGroupRoot = NULL;
2485 RTListInit(&pThis->LstBlockGroupLru);
2486
2487 /* Destroy the inode tree. */
2488 RTAvlU32Destroy(&pThis->InodeRoot, rtFsExtVolInodeTreeDestroy, pThis);
2489 pThis->InodeRoot = NULL;
2490 RTListInit(&pThis->LstInodeLru);
2491
2492 /* Destroy the block cache tree. */
2493 RTAvlU64Destroy(&pThis->BlockRoot, rtFsExtVolBlockTreeDestroy, pThis);
2494 pThis->BlockRoot = NULL;
2495 RTListInit(&pThis->LstBlockLru);
2496
2497 /*
2498 * Backing file and handles.
2499 */
2500 RTVfsFileRelease(pThis->hVfsBacking);
2501 pThis->hVfsBacking = NIL_RTVFSFILE;
2502 pThis->hVfsSelf = NIL_RTVFS;
2503
2504 return VINF_SUCCESS;
2505}
2506
2507
2508/**
2509 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
2510 */
2511static DECLCALLBACK(int) rtFsExtVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2512{
2513 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
2514 return VERR_WRONG_TYPE;
2515}
2516
2517
2518/**
2519 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
2520 */
2521static DECLCALLBACK(int) rtFsExtVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
2522{
2523 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2524 int rc = rtFsExtVol_OpenDirByInode(pThis, EXT_INODE_NR_ROOT_DIR, phVfsDir);
2525 LogFlowFunc(("returns %Rrc\n", rc));
2526 return rc;
2527}
2528
2529
2530/**
2531 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
2532 */
2533static DECLCALLBACK(int) rtFsExtVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
2534{
2535 int rc = VINF_SUCCESS;
2536 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2537
2538 *pfUsed = false;
2539
2540 uint64_t iBlock = rtFsExtDiskOffsetToBlockIdx(pThis, off);
2541 uint64_t cBlocks = rtFsExtDiskOffsetToBlockIdx(pThis, cb) + (cb % pThis->cbBlock ? 1 : 0);
2542 while (cBlocks > 0)
2543 {
2544 uint32_t const iBlockGroup = iBlock / pThis->cBlocksPerGroup;
2545 uint32_t const iBlockRelStart = iBlock - iBlockGroup * pThis->cBlocksPerGroup;
2546 PRTFSEXTBLKGRP pBlockGroup = NULL;
2547
2548 rc = rtFsExtBlockGroupLoad(pThis, iBlockGroup, &pBlockGroup);
2549 if (RT_FAILURE(rc))
2550 break;
2551
2552 uint64_t cBlocksThis = RT_MIN(cBlocks, iBlockRelStart - pThis->cBlocksPerGroup);
2553 if (rtFsExtIsBlockRangeInUse(pBlockGroup, iBlockRelStart, cBlocksThis))
2554 {
2555 *pfUsed = true;
2556 break;
2557 }
2558
2559 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
2560 cBlocks -= cBlocksThis;
2561 iBlock += cBlocksThis;
2562 }
2563
2564 return rc;
2565}
2566
2567
2568DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsExtVolOps =
2569{
2570 /* .Obj = */
2571 {
2572 /* .uVersion = */ RTVFSOBJOPS_VERSION,
2573 /* .enmType = */ RTVFSOBJTYPE_VFS,
2574 /* .pszName = */ "ExtVol",
2575 /* .pfnClose = */ rtFsExtVol_Close,
2576 /* .pfnQueryInfo = */ rtFsExtVol_QueryInfo,
2577 /* .pfnQueryInfoEx = */ NULL,
2578 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
2579 },
2580 /* .uVersion = */ RTVFSOPS_VERSION,
2581 /* .fFeatures = */ 0,
2582 /* .pfnOpenRoot = */ rtFsExtVol_OpenRoot,
2583 /* .pfnQueryRangeState = */ rtFsExtVol_QueryRangeState,
2584 /* .uEndMarker = */ RTVFSOPS_VERSION
2585};
2586
2587
2588
2589/**
2590 * Loads the parameters from the given original ext2 format superblock (EXT_SB_REV_ORIG).
2591 *
2592 * @returns IPRT status code.
2593 * @param pThis The ext volume instance.
2594 * @param pSb The superblock to load.
2595 * @param pErrInfo Where to return additional error info.
2596 */
2597static int rtFsExtVolLoadAndParseSuperBlockV0(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
2598{
2599 RT_NOREF(pErrInfo);
2600
2601 /*
2602 * Linux never supported a differing cluster (also called fragment) size for
2603 * the original ext2 layout so we reject such filesystems as it is not clear what
2604 * the purpose is really.
2605 */
2606 if (RT_LE2H_U32(pSb->cLogBlockSize) != RT_LE2H_U32(pSb->cLogClusterSize))
2607 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem cluster and block size differ");
2608
2609 pThis->f64Bit = false;
2610 pThis->cBlockShift = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
2611 pThis->cbBlock = UINT64_C(1) << pThis->cBlockShift;
2612 pThis->cbInode = sizeof(EXTINODE);
2613 pThis->cbBlkGrpDesc = sizeof(EXTBLOCKGROUPDESC32);
2614 pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
2615 pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
2616 pThis->cBlockGroups = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
2617 pThis->cbBlockBitmap = pThis->cBlocksPerGroup / 8;
2618 if (pThis->cBlocksPerGroup % 8)
2619 pThis->cbBlockBitmap++;
2620 pThis->cbInodeBitmap = pThis->cInodesPerGroup / 8;
2621 if (pThis->cInodesPerGroup % 8)
2622 pThis->cbInodeBitmap++;
2623
2624 return VINF_SUCCESS;
2625}
2626
2627
2628/**
2629 * Loads the parameters from the given ext superblock (EXT_SB_REV_V2_DYN_INODE_SZ).
2630 *
2631 * @returns IPRT status code.
2632 * @param pThis The ext volume instance.
2633 * @param pSb The superblock to load.
2634 * @param pErrInfo Where to return additional error info.
2635 */
2636static int rtFsExtVolLoadAndParseSuperBlockV1(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
2637{
2638 if ((RT_LE2H_U32(pSb->fFeaturesIncompat) & ~RTFSEXT_INCOMPAT_FEATURES_SUPP) != 0)
2639 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported incompatible features: %RX32",
2640 RT_LE2H_U32(pSb->fFeaturesIncompat) & ~RTFSEXT_INCOMPAT_FEATURES_SUPP);
2641 if ( RT_LE2H_U32(pSb->fFeaturesCompatRo) != 0
2642 && !(pThis->fMntFlags & RTVFSMNT_F_READ_ONLY))
2643 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported readonly features: %RX32",
2644 RT_LE2H_U32(pSb->fFeaturesCompatRo));
2645
2646 pThis->fFeaturesIncompat = RT_LE2H_U32(pSb->fFeaturesIncompat);
2647 pThis->f64Bit = RT_BOOL(pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_64BIT);
2648 pThis->cBlockShift = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
2649 pThis->cbBlock = UINT64_C(1) << pThis->cBlockShift;
2650 pThis->cbInode = RT_LE2H_U16(pSb->cbInode);
2651 pThis->cbBlkGrpDesc = pThis->f64Bit ? RT_LE2H_U16(pSb->cbGroupDesc) : sizeof(EXTBLOCKGROUPDESC32);
2652 pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
2653 pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
2654 pThis->cBlockGroups = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
2655 pThis->cbBlockBitmap = pThis->cBlocksPerGroup / 8;
2656 if (pThis->cBlocksPerGroup % 8)
2657 pThis->cbBlockBitmap++;
2658 pThis->cbInodeBitmap = pThis->cInodesPerGroup / 8;
2659 if (pThis->cInodesPerGroup % 8)
2660 pThis->cbInodeBitmap++;
2661
2662 return VINF_SUCCESS;
2663}
2664
2665
2666/**
2667 * Loads and parses the superblock of the filesystem.
2668 *
2669 * @returns IPRT status code.
2670 * @param pThis The ext volume instance.
2671 * @param pErrInfo Where to return additional error info.
2672 */
2673static int rtFsExtVolLoadAndParseSuperblock(PRTFSEXTVOL pThis, PRTERRINFO pErrInfo)
2674{
2675 int rc = VINF_SUCCESS;
2676 EXTSUPERBLOCK Sb;
2677 rc = RTVfsFileReadAt(pThis->hVfsBacking, EXT_SB_OFFSET, &Sb, sizeof(EXTSUPERBLOCK), NULL);
2678 if (RT_FAILURE(rc))
2679 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
2680
2681 /* Validate the superblock. */
2682 if (RT_LE2H_U16(Sb.u16Signature) != EXT_SB_SIGNATURE)
2683 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not EXT - Signature mismatch: %RX16", RT_LE2H_U16(Sb.u16Signature));
2684
2685#ifdef LOG_ENABLED
2686 rtFsExtSb_Log(&Sb);
2687#endif
2688
2689 if (RT_LE2H_U16(Sb.u16FilesystemState) == EXT_SB_STATE_ERRORS)
2690 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains errors");
2691
2692 if (RT_LE2H_U32(Sb.u32RevLvl) == EXT_SB_REV_ORIG)
2693 rc = rtFsExtVolLoadAndParseSuperBlockV0(pThis, &Sb, pErrInfo);
2694 else
2695 rc = rtFsExtVolLoadAndParseSuperBlockV1(pThis, &Sb, pErrInfo);
2696
2697 return rc;
2698}
2699
2700
2701RTDECL(int) RTFsExtVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fExtFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
2702{
2703 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
2704 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
2705 AssertReturn(!fExtFlags, VERR_INVALID_FLAGS);
2706
2707 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
2708 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2709
2710 /*
2711 * Create a VFS instance and initialize the data so rtFsExtVol_Close works.
2712 */
2713 RTVFS hVfs;
2714 PRTFSEXTVOL pThis;
2715 int rc = RTVfsNew(&g_rtFsExtVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
2716 if (RT_SUCCESS(rc))
2717 {
2718 pThis->hVfsBacking = hVfsFileIn;
2719 pThis->hVfsSelf = hVfs;
2720 pThis->fMntFlags = fMntFlags;
2721 pThis->fExtFlags = fExtFlags;
2722 pThis->BlockGroupRoot = NULL;
2723 pThis->InodeRoot = NULL;
2724 pThis->BlockRoot = NULL;
2725 pThis->cbBlockGroups = 0;
2726 pThis->cbInodes = 0;
2727 pThis->cbBlocks = 0;
2728 RTListInit(&pThis->LstBlockGroupLru);
2729 RTListInit(&pThis->LstInodeLru);
2730 RTListInit(&pThis->LstBlockLru);
2731
2732 rc = RTVfsFileQuerySize(pThis->hVfsBacking, &pThis->cbBacking);
2733 if (RT_SUCCESS(rc))
2734 {
2735 rc = rtFsExtVolLoadAndParseSuperblock(pThis, pErrInfo);
2736 if (RT_SUCCESS(rc))
2737 {
2738 *phVfs = hVfs;
2739 return VINF_SUCCESS;
2740 }
2741 }
2742
2743 RTVfsRelease(hVfs);
2744 *phVfs = NIL_RTVFS;
2745 }
2746 else
2747 RTVfsFileRelease(hVfsFileIn);
2748
2749 return rc;
2750}
2751
2752
2753/**
2754 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
2755 */
2756static DECLCALLBACK(int) rtVfsChainExtVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
2757 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
2758{
2759 RT_NOREF(pProviderReg);
2760
2761 /*
2762 * Basic checks.
2763 */
2764 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
2765 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
2766 if ( pElement->enmType != RTVFSOBJTYPE_VFS
2767 && pElement->enmType != RTVFSOBJTYPE_DIR)
2768 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
2769 if (pElement->cArgs > 1)
2770 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
2771
2772 /*
2773 * Parse the flag if present, save in pElement->uProvider.
2774 */
2775 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
2776 if (pElement->cArgs > 0)
2777 {
2778 const char *psz = pElement->paArgs[0].psz;
2779 if (*psz)
2780 {
2781 if (!strcmp(psz, "ro"))
2782 fReadOnly = true;
2783 else if (!strcmp(psz, "rw"))
2784 fReadOnly = false;
2785 else
2786 {
2787 *poffError = pElement->paArgs[0].offSpec;
2788 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
2789 }
2790 }
2791 }
2792
2793 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
2794 return VINF_SUCCESS;
2795}
2796
2797
2798/**
2799 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2800 */
2801static DECLCALLBACK(int) rtVfsChainExtVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2802 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2803 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2804{
2805 RT_NOREF(pProviderReg, pSpec, poffError);
2806
2807 int rc;
2808 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2809 if (hVfsFileIn != NIL_RTVFSFILE)
2810 {
2811 RTVFS hVfs;
2812 rc = RTFsExtVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
2813 RTVfsFileRelease(hVfsFileIn);
2814 if (RT_SUCCESS(rc))
2815 {
2816 *phVfsObj = RTVfsObjFromVfs(hVfs);
2817 RTVfsRelease(hVfs);
2818 if (*phVfsObj != NIL_RTVFSOBJ)
2819 return VINF_SUCCESS;
2820 rc = VERR_VFS_CHAIN_CAST_FAILED;
2821 }
2822 }
2823 else
2824 rc = VERR_VFS_CHAIN_CAST_FAILED;
2825 return rc;
2826}
2827
2828
2829/**
2830 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2831 */
2832static DECLCALLBACK(bool) rtVfsChainExtVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2833 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2834 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2835{
2836 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2837 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2838 || !pReuseElement->paArgs[0].uProvider)
2839 return true;
2840 return false;
2841}
2842
2843
2844/** VFS chain element 'ext'. */
2845static RTVFSCHAINELEMENTREG g_rtVfsChainExtVolReg =
2846{
2847 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2848 /* fReserved = */ 0,
2849 /* pszName = */ "ext",
2850 /* ListEntry = */ { NULL, NULL },
2851 /* pszHelp = */ "Open a EXT file system, requires a file object on the left side.\n"
2852 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
2853 /* pfnValidate = */ rtVfsChainExtVol_Validate,
2854 /* pfnInstantiate = */ rtVfsChainExtVol_Instantiate,
2855 /* pfnCanReuseElement = */ rtVfsChainExtVol_CanReuseElement,
2856 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2857};
2858
2859RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainExtVolReg, rtVfsChainExtVolReg);
2860
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