VirtualBox

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

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

*: Preparing for iprt/vfslowlevel.h to no longer include iprt/err.h. bugref:9344

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