VirtualBox

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

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

Runtime/fs/vfsext: Implement a small block cache for caching block maps or extents to speed up I/O

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