VirtualBox

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

Last change on this file since 82972 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.3 KB
Line 
1/* $Id: extvfs.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * IPRT - Ext2/3/4 Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2012-2020 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_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 if (RT_SUCCESS(rc))
1197 {
1198 bool fIns = RTAvlU32Insert(&pThis->InodeRoot, &pInode->Core);
1199 Assert(fIns); RT_NOREF(fIns);
1200 }
1201 }
1202 else
1203 rc = VERR_NO_MEMORY;
1204 }
1205 else
1206 {
1207 /* Remove from current LRU list position and add to the beginning. */
1208 uint32_t cRefs = ASMAtomicIncU32(&pInode->cRefs);
1209 if (cRefs == 1) /* Inodes get removed from the LRU list if they are referenced. */
1210 RTListNodeRemove(&pInode->NdLru);
1211 }
1212
1213 if (RT_SUCCESS(rc))
1214 *ppInode = pInode;
1215 else if (pInode)
1216 {
1217 ASMAtomicDecU32(&pInode->cRefs);
1218 rtFsExtInodeFree(pThis, pInode); /* Free the inode. */
1219 }
1220
1221 return rc;
1222}
1223
1224
1225/**
1226 * Releases a reference of the given inode.
1227 *
1228 * @returns nothing.
1229 * @param pThis The ext volume instance.
1230 * @param pInode The inode to release.
1231 */
1232static void rtFsExtInodeRelease(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
1233{
1234 uint32_t cRefs = ASMAtomicDecU32(&pInode->cRefs);
1235 if (!cRefs)
1236 rtFsExtInodeFree(pThis, pInode);
1237}
1238
1239
1240/**
1241 * Worker for various QueryInfo methods.
1242 *
1243 * @returns IPRT status code.
1244 * @param pInode The inode structure to return info for.
1245 * @param pObjInfo Where to return object info.
1246 * @param enmAddAttr What additional info to return.
1247 */
1248static int rtFsExtInode_QueryInfo(PRTFSEXTINODE pInode, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1249{
1250 RT_ZERO(*pObjInfo);
1251
1252 pObjInfo->cbObject = pInode->ObjInfo.cbObject;
1253 pObjInfo->cbAllocated = pInode->ObjInfo.cbAllocated;
1254 pObjInfo->AccessTime = pInode->ObjInfo.AccessTime;
1255 pObjInfo->ModificationTime = pInode->ObjInfo.ModificationTime;
1256 pObjInfo->ChangeTime = pInode->ObjInfo.ChangeTime;
1257 pObjInfo->BirthTime = pInode->ObjInfo.BirthTime;
1258 pObjInfo->Attr.fMode = pInode->ObjInfo.Attr.fMode;
1259 pObjInfo->Attr.enmAdditional = enmAddAttr;
1260 switch (enmAddAttr)
1261 {
1262 case RTFSOBJATTRADD_UNIX:
1263 memcpy(&pObjInfo->Attr.u.Unix, &pInode->ObjInfo.Attr.u.Unix, sizeof(pInode->ObjInfo.Attr.u.Unix));
1264 break;
1265
1266 case RTFSOBJATTRADD_UNIX_OWNER:
1267 pObjInfo->Attr.u.UnixOwner.uid = pInode->ObjInfo.Attr.u.Unix.uid;
1268 break;
1269
1270 case RTFSOBJATTRADD_UNIX_GROUP:
1271 pObjInfo->Attr.u.UnixGroup.gid = pInode->ObjInfo.Attr.u.Unix.gid;
1272 break;
1273
1274 default:
1275 break;
1276 }
1277
1278 return VINF_SUCCESS;
1279}
1280
1281
1282/**
1283 * Validates a given extent header.
1284 *
1285 * @returns Flag whether the extent header appears to be valid.
1286 * @param pExtentHdr The extent header to validate.
1287 */
1288DECLINLINE(bool) rtFsExtInode_ExtentHdrValidate(PCEXTEXTENTHDR pExtentHdr)
1289{
1290 return RT_LE2H_U16(pExtentHdr->u16Magic) == EXT_EXTENT_HDR_MAGIC
1291 && RT_LE2H_U16(pExtentHdr->cEntries) <= RT_LE2H_U16(pExtentHdr->cMax)
1292 && RT_LE2H_U16(pExtentHdr->uDepth) <= EXT_EXTENT_HDR_DEPTH_MAX;
1293}
1294
1295
1296/**
1297 * Parses the given extent, checking whether it intersects with the given block.
1298 *
1299 * @returns Flag whether the extent maps the given range (at least partly).
1300 * @param pExtent The extent to parse.
1301 * @param iBlock The starting inode block to map.
1302 * @param cBlocks Number of blocks requested.
1303 * @param piBlockFs Where to store the filesystem block on success.
1304 * @param pcBlocks Where to store the number of contiguous blocks on success.
1305 * @param pfSparse Where to store the sparse flag on success.
1306 */
1307DECLINLINE(bool) rtFsExtInode_ExtentParse(PCEXTEXTENT pExtent, uint64_t iBlock, size_t cBlocks,
1308 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1309{
1310#ifdef LOG_ENABLED
1311 rtFsExtExtent_Log(pExtent);
1312#endif
1313
1314 uint32_t iExtentBlock = RT_LE2H_U32(pExtent->iBlock);
1315 uint16_t cExtentLength = RT_LE2H_U16(pExtent->cBlocks);
1316
1317 /* Length over EXT_EXTENT_LENGTH_LIMIT blocks indicate a sparse extent. */
1318 if (cExtentLength > EXT_EXTENT_LENGTH_LIMIT)
1319 {
1320 *pfSparse = true;
1321 cExtentLength -= EXT_EXTENT_LENGTH_LIMIT;
1322 }
1323 else
1324 *pfSparse = false;
1325
1326 if ( iExtentBlock <= iBlock
1327 && iExtentBlock + cExtentLength > iBlock)
1328 {
1329 uint32_t iBlockRel = iBlock - iExtentBlock;
1330 *pcBlocks = RT_MIN(cBlocks, cExtentLength - iBlockRel);
1331 *piBlockFs = ( ((uint64_t)RT_LE2H_U16(pExtent->offStartHigh)) << 32
1332 | ((uint64_t)RT_LE2H_U32(pExtent->offStartLow))) + iBlockRel;
1333 return true;
1334 }
1335
1336 return false;
1337}
1338
1339
1340/**
1341 * Locates the location of the next level in the extent tree mapping the given block.
1342 *
1343 * @returns Filesystem block number where the next level of the extent is stored.
1344 * @param paExtentIdx Pointer to the array of extent index nodes.
1345 * @param cEntries Number of entries in the extent index node array.
1346 * @param iBlock The block to resolve.
1347 */
1348DECLINLINE(uint64_t) rtFsExtInode_ExtentIndexLocateNextLvl(PCEXTEXTENTIDX paExtentIdx, uint16_t cEntries, uint64_t iBlock)
1349{
1350 for (uint32_t i = 1; i < cEntries; i++)
1351 {
1352 PCEXTEXTENTIDX pPrev = &paExtentIdx[i - 1];
1353 PCEXTEXTENTIDX pCur = &paExtentIdx[i];
1354
1355#ifdef LOG_ENABLED
1356 rtFsExtExtentIdx_Log(pPrev);
1357#endif
1358
1359 if ( RT_LE2H_U32(pPrev->iBlock) <= iBlock
1360 && RT_LE2H_U32(pCur->iBlock) > iBlock)
1361 return (uint64_t)RT_LE2H_U16(pPrev->offChildHigh) << 32
1362 | (uint64_t)RT_LE2H_U32(pPrev->offChildLow);
1363 }
1364
1365 /* Nothing found so far, the blast extent index must cover the block as the array is sorted. */
1366 PCEXTEXTENTIDX pLast = &paExtentIdx[cEntries - 1];
1367#ifdef LOG_ENABLED
1368 rtFsExtExtentIdx_Log(pLast);
1369#endif
1370
1371 return (uint64_t)RT_LE2H_U16(pLast->offChildHigh) << 32
1372 | (uint64_t)RT_LE2H_U32(pLast->offChildLow);
1373}
1374
1375
1376/**
1377 * Maps the given inode block to the destination filesystem block using the embedded extent tree.
1378 *
1379 * @returns IPRT status code.
1380 * @param pThis The ext volume instance.
1381 * @param pInode The inode structure to read from.
1382 * @param iBlock The starting inode block to map.
1383 * @param cBlocks Number of blocks requested.
1384 * @param piBlockFs Where to store the filesystem block on success.
1385 * @param pcBlocks Where to store the number of contiguous blocks on success.
1386 * @param pfSparse Where to store the sparse flag on success.
1387 *
1388 * @todo Optimize
1389 */
1390static int rtFsExtInode_MapBlockToFsViaExtent(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1391 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1392{
1393 int rc = VINF_SUCCESS;
1394
1395 /* The root of the extent tree is located in the block data of the inode. */
1396 PCEXTEXTENTHDR pExtentHdr = (PCEXTEXTENTHDR)&pInode->aiBlocks[0];
1397
1398#ifdef LOG_ENABLED
1399 rtFsExtExtentHdr_Log(pExtentHdr);
1400#endif
1401
1402 /*
1403 * Some validation, the top level is located inside the inode block data
1404 * and has a maxmimum of 4 entries.
1405 */
1406 if ( rtFsExtInode_ExtentHdrValidate(pExtentHdr)
1407 && RT_LE2H_U16(pExtentHdr->cMax) <= 4)
1408 {
1409 uint16_t uDepthCur = RT_LE2H_U16(pExtentHdr->uDepth);
1410 if (!uDepthCur)
1411 {
1412 PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
1413
1414 rc = VERR_VFS_BOGUS_FORMAT;
1415 for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
1416 {
1417 /* Check whether the extent intersects with the block. */
1418 if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
1419 {
1420 rc = VINF_SUCCESS;
1421 break;
1422 }
1423 pExtent++;
1424 }
1425 }
1426 else
1427 {
1428 uint8_t *pbExtent = NULL;
1429 PRTFSEXTBLOCKENTRY pBlock = NULL;
1430 uint64_t iBlockNext = 0;
1431 PCEXTEXTENTIDX paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
1432 uint16_t cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
1433
1434 /* Descend the tree until we reached the leaf nodes. */
1435 do
1436 {
1437 iBlockNext = rtFsExtInode_ExtentIndexLocateNextLvl(paExtentIdx, cEntries, iBlock);
1438 /* Read in the full block. */
1439 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&pbExtent);
1440 if (RT_SUCCESS(rc))
1441 {
1442 pExtentHdr = (PCEXTEXTENTHDR)pbExtent;
1443
1444#ifdef LOG_ENABLED
1445 rtFsExtExtentHdr_Log(pExtentHdr);
1446#endif
1447
1448 if ( rtFsExtInode_ExtentHdrValidate(pExtentHdr)
1449 && RT_LE2H_U16(pExtentHdr->cMax) <= (pThis->cbBlock - sizeof(EXTEXTENTHDR)) / sizeof(EXTEXTENTIDX)
1450 && RT_LE2H_U16(pExtentHdr->uDepth) == uDepthCur - 1)
1451 {
1452 uDepthCur--;
1453 cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
1454 paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
1455 if (uDepthCur)
1456 rtFsExtVol_BlockRelease(pThis, pBlock);
1457 }
1458 else
1459 rc = VERR_VFS_BOGUS_FORMAT;
1460 }
1461 }
1462 while ( uDepthCur > 0
1463 && RT_SUCCESS(rc));
1464
1465 if (RT_SUCCESS(rc))
1466 {
1467 Assert(!uDepthCur);
1468
1469 /* We reached the leaf nodes. */
1470 PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
1471 for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
1472 {
1473 /* Check whether the extent intersects with the block. */
1474 if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
1475 {
1476 rc = VINF_SUCCESS;
1477 break;
1478 }
1479 pExtent++;
1480 }
1481 }
1482
1483 if (pBlock)
1484 rtFsExtVol_BlockRelease(pThis, pBlock);
1485 }
1486 }
1487 else
1488 rc = VERR_VFS_BOGUS_FORMAT;
1489
1490 return rc;
1491}
1492
1493
1494/**
1495 * Maps the given inode block to the destination filesystem block using the original block mapping scheme.
1496 *
1497 * @returns IPRT status code.
1498 * @param pThis The ext volume instance.
1499 * @param pInode The inode structure to read from.
1500 * @param iBlock The inode block to map.
1501 * @param cBlocks Number of blocks requested.
1502 * @param piBlockFs Where to store the filesystem block on success.
1503 * @param pcBlocks Where to store the number of contiguous blocks on success.
1504 * @param pfSparse Where to store the sparse flag on success.
1505 *
1506 * @todo Optimize and handle sparse files.
1507 */
1508static int rtFsExtInode_MapBlockToFsViaBlockMap(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1509 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1510{
1511 int rc = VINF_SUCCESS;
1512 RT_NOREF(cBlocks);
1513
1514 *pfSparse = false;
1515 *pcBlocks = 1;
1516
1517 /* The first 12 inode blocks are directly mapped from the inode. */
1518 if (iBlock <= 11)
1519 *piBlockFs = pInode->aiBlocks[iBlock];
1520 else
1521 {
1522 uint32_t cEntriesPerBlockMap = (uint32_t)(pThis->cbBlock >> sizeof(uint32_t));
1523
1524 if (iBlock <= cEntriesPerBlockMap + 11)
1525 {
1526 /* Indirect block. */
1527 PRTFSEXTBLOCKENTRY pBlock = NULL;
1528 uint32_t *paBlockMap = NULL;
1529 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[12], &pBlock, (void **)&paBlockMap);
1530 if (RT_SUCCESS(rc))
1531 {
1532 *piBlockFs = RT_LE2H_U32(paBlockMap[iBlock - 12]);
1533 rtFsExtVol_BlockRelease(pThis, pBlock);
1534 }
1535 }
1536 else if (iBlock <= cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap + 11)
1537 {
1538 /* Double indirect block. */
1539 PRTFSEXTBLOCKENTRY pBlock = NULL;
1540 uint32_t *paBlockMap = NULL;
1541
1542 iBlock -= 12 + cEntriesPerBlockMap;
1543 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[13], &pBlock, (void **)&paBlockMap);
1544 if (RT_SUCCESS(rc))
1545 {
1546 uint32_t idxBlockL2 = iBlock / cEntriesPerBlockMap;
1547 uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
1548 uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
1549
1550 rtFsExtVol_BlockRelease(pThis, pBlock);
1551 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1552 if (RT_SUCCESS(rc))
1553 {
1554 *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
1555 rtFsExtVol_BlockRelease(pThis, pBlock);
1556 }
1557 }
1558 }
1559 else
1560 {
1561 /* Triple indirect block. */
1562 PRTFSEXTBLOCKENTRY pBlock = NULL;
1563 uint32_t *paBlockMap = NULL;
1564
1565 iBlock -= 12 + cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap;
1566 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[14], &pBlock, (void **)&paBlockMap);
1567 if (RT_SUCCESS(rc))
1568 {
1569 uint32_t idxBlockL3 = iBlock / (cEntriesPerBlockMap * cEntriesPerBlockMap);
1570 uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL3]);
1571
1572 rtFsExtVol_BlockRelease(pThis, pBlock);
1573 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1574 if (RT_SUCCESS(rc))
1575 {
1576 uint32_t idxBlockL2 = (iBlock % (cEntriesPerBlockMap * cEntriesPerBlockMap)) / cEntriesPerBlockMap;
1577 uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
1578 iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
1579
1580 rtFsExtVol_BlockRelease(pThis, pBlock);
1581 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1582 if (RT_SUCCESS(rc))
1583 {
1584 *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
1585 rtFsExtVol_BlockRelease(pThis, pBlock);
1586 }
1587 }
1588 }
1589 }
1590 }
1591
1592 return rc;
1593}
1594
1595
1596/**
1597 * Maps the given inode block to the destination filesystem block.
1598 *
1599 * @returns IPRT status code.
1600 * @param pThis The ext volume instance.
1601 * @param pInode The inode structure to read from.
1602 * @param iBlock The inode block to map.
1603 * @param cBlocks Number of blocks requested.
1604 * @param piBlockFs Where to store the filesystem block on success.
1605 * @param pcBlocks Where to store the number of contiguous blocks on success.
1606 * @param pfSparse Where to store the sparse flag on success.
1607 *
1608 * @todo Optimize
1609 */
1610static int rtFsExtInode_MapBlockToFs(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1611 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1612{
1613 if (pInode->fFlags & EXT_INODE_F_EXTENTS)
1614 return rtFsExtInode_MapBlockToFsViaExtent(pThis, pInode, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse);
1615 else
1616 return rtFsExtInode_MapBlockToFsViaBlockMap(pThis, pInode, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse);
1617}
1618
1619
1620/**
1621 * Reads data from the given inode at the given byte offset.
1622 *
1623 * @returns IPRT status code.
1624 * @param pThis The ext volume instance.
1625 * @param pInode The inode structure to read from.
1626 * @param off The byte offset to start reading from.
1627 * @param pvBuf Where to store the read data to.
1628 * @param pcbRead Where to return the amount of data read.
1629 */
1630static int rtFsExtInode_Read(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t off, void *pvBuf, size_t cbRead, size_t *pcbRead)
1631{
1632 int rc = VINF_SUCCESS;
1633 uint8_t *pbBuf = (uint8_t *)pvBuf;
1634
1635 if (((uint64_t)pInode->ObjInfo.cbObject < off + cbRead))
1636 {
1637 if (!pcbRead)
1638 return VERR_EOF;
1639 else
1640 cbRead = (uint64_t)pInode->ObjInfo.cbObject - off;
1641 }
1642
1643 while ( cbRead
1644 && RT_SUCCESS(rc))
1645 {
1646 uint64_t iBlockStart = rtFsExtDiskOffsetToBlockIdx(pThis, off);
1647 uint32_t offBlockStart = off % pThis->cbBlock;
1648
1649 /* Resolve the inode block to the proper filesystem block. */
1650 uint64_t iBlockFs = 0;
1651 size_t cBlocks = 0;
1652 bool fSparse = false;
1653 rc = rtFsExtInode_MapBlockToFs(pThis, pInode, iBlockStart, 1, &iBlockFs, &cBlocks, &fSparse);
1654 if (RT_SUCCESS(rc))
1655 {
1656 Assert(cBlocks == 1);
1657
1658 size_t cbThisRead = RT_MIN(cbRead, pThis->cbBlock - offBlockStart);
1659
1660 if (!fSparse)
1661 {
1662 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, iBlockFs);
1663 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead + offBlockStart, pbBuf, cbThisRead, NULL);
1664 }
1665 else
1666 memset(pbBuf, 0, cbThisRead);
1667
1668 if (RT_SUCCESS(rc))
1669 {
1670 pbBuf += cbThisRead;
1671 cbRead -= cbThisRead;
1672 off += cbThisRead;
1673 if (pcbRead)
1674 *pcbRead += cbThisRead;
1675 }
1676 }
1677 }
1678
1679 return rc;
1680}
1681
1682
1683
1684/*
1685 *
1686 * File operations.
1687 * File operations.
1688 * File operations.
1689 *
1690 */
1691
1692/**
1693 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1694 */
1695static DECLCALLBACK(int) rtFsExtFile_Close(void *pvThis)
1696{
1697 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1698 LogFlow(("rtFsExtFile_Close(%p/%p)\n", pThis, pThis->pInode));
1699
1700 rtFsExtInodeRelease(pThis->pVol, pThis->pInode);
1701 pThis->pInode = NULL;
1702 pThis->pVol = NULL;
1703 return VINF_SUCCESS;
1704}
1705
1706
1707/**
1708 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1709 */
1710static DECLCALLBACK(int) rtFsExtFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1711{
1712 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1713 return rtFsExtInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
1714}
1715
1716
1717/**
1718 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1719 */
1720static DECLCALLBACK(int) rtFsExtFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1721{
1722 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1723 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
1724 RT_NOREF(fBlocking);
1725
1726 if (off == -1)
1727 off = pThis->offFile;
1728 else
1729 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
1730
1731 int rc;
1732 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
1733 if (!pcbRead)
1734 {
1735 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1736 if (RT_SUCCESS(rc))
1737 pThis->offFile = off + cbRead;
1738 Log6(("rtFsExtFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
1739 }
1740 else
1741 {
1742 PRTFSEXTINODE pInode = pThis->pInode;
1743 if (off >= pInode->ObjInfo.cbObject)
1744 {
1745 *pcbRead = 0;
1746 rc = VINF_EOF;
1747 }
1748 else
1749 {
1750 if ((uint64_t)off + cbRead <= (uint64_t)pInode->ObjInfo.cbObject)
1751 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1752 else
1753 {
1754 /* Return VINF_EOF if beyond end-of-file. */
1755 cbRead = (size_t)(pInode->ObjInfo.cbObject - off);
1756 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1757 if (RT_SUCCESS(rc))
1758 rc = VINF_EOF;
1759 }
1760 if (RT_SUCCESS(rc))
1761 {
1762 pThis->offFile = off + cbRead;
1763 *pcbRead = cbRead;
1764 }
1765 else
1766 *pcbRead = 0;
1767 }
1768 Log6(("rtFsExtFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
1769 }
1770
1771 return rc;
1772}
1773
1774
1775/**
1776 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1777 */
1778static DECLCALLBACK(int) rtFsExtFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1779{
1780 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
1781 return VERR_WRITE_PROTECT;
1782}
1783
1784
1785/**
1786 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1787 */
1788static DECLCALLBACK(int) rtFsExtFile_Flush(void *pvThis)
1789{
1790 RT_NOREF(pvThis);
1791 return VINF_SUCCESS;
1792}
1793
1794
1795/**
1796 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1797 */
1798static DECLCALLBACK(int) rtFsExtFile_Tell(void *pvThis, PRTFOFF poffActual)
1799{
1800 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1801 *poffActual = pThis->offFile;
1802 return VINF_SUCCESS;
1803}
1804
1805
1806/**
1807 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1808 */
1809static DECLCALLBACK(int) rtFsExtFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1810{
1811 RT_NOREF(pvThis, fMode, fMask);
1812 return VERR_WRITE_PROTECT;
1813}
1814
1815
1816/**
1817 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1818 */
1819static DECLCALLBACK(int) rtFsExtFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1820 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1821{
1822 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1823 return VERR_WRITE_PROTECT;
1824}
1825
1826
1827/**
1828 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1829 */
1830static DECLCALLBACK(int) rtFsExtFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1831{
1832 RT_NOREF(pvThis, uid, gid);
1833 return VERR_WRITE_PROTECT;
1834}
1835
1836
1837/**
1838 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1839 */
1840static DECLCALLBACK(int) rtFsExtFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1841{
1842 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1843 RTFOFF offNew;
1844 switch (uMethod)
1845 {
1846 case RTFILE_SEEK_BEGIN:
1847 offNew = offSeek;
1848 break;
1849 case RTFILE_SEEK_END:
1850 offNew = pThis->pInode->ObjInfo.cbObject + offSeek;
1851 break;
1852 case RTFILE_SEEK_CURRENT:
1853 offNew = (RTFOFF)pThis->offFile + offSeek;
1854 break;
1855 default:
1856 return VERR_INVALID_PARAMETER;
1857 }
1858 if (offNew >= 0)
1859 {
1860 pThis->offFile = offNew;
1861 *poffActual = offNew;
1862 return VINF_SUCCESS;
1863 }
1864 return VERR_NEGATIVE_SEEK;
1865}
1866
1867
1868/**
1869 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1870 */
1871static DECLCALLBACK(int) rtFsExtFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1872{
1873 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1874 *pcbFile = (uint64_t)pThis->pInode->ObjInfo.cbObject;
1875 return VINF_SUCCESS;
1876}
1877
1878
1879/**
1880 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
1881 */
1882static DECLCALLBACK(int) rtFsExtFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
1883{
1884 RT_NOREF(pvThis, cbFile, fFlags);
1885 return VERR_WRITE_PROTECT;
1886}
1887
1888
1889/**
1890 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
1891 */
1892static DECLCALLBACK(int) rtFsExtFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
1893{
1894 RT_NOREF(pvThis);
1895 *pcbMax = INT64_MAX; /** @todo */
1896 return VINF_SUCCESS;
1897}
1898
1899
1900/**
1901 * EXT file operations.
1902 */
1903static const RTVFSFILEOPS g_rtFsExtFileOps =
1904{
1905 { /* Stream */
1906 { /* Obj */
1907 RTVFSOBJOPS_VERSION,
1908 RTVFSOBJTYPE_FILE,
1909 "EXT File",
1910 rtFsExtFile_Close,
1911 rtFsExtFile_QueryInfo,
1912 RTVFSOBJOPS_VERSION
1913 },
1914 RTVFSIOSTREAMOPS_VERSION,
1915 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1916 rtFsExtFile_Read,
1917 rtFsExtFile_Write,
1918 rtFsExtFile_Flush,
1919 NULL /*PollOne*/,
1920 rtFsExtFile_Tell,
1921 NULL /*pfnSkip*/,
1922 NULL /*pfnZeroFill*/,
1923 RTVFSIOSTREAMOPS_VERSION,
1924 },
1925 RTVFSFILEOPS_VERSION,
1926 0,
1927 { /* ObjSet */
1928 RTVFSOBJSETOPS_VERSION,
1929 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1930 rtFsExtFile_SetMode,
1931 rtFsExtFile_SetTimes,
1932 rtFsExtFile_SetOwner,
1933 RTVFSOBJSETOPS_VERSION
1934 },
1935 rtFsExtFile_Seek,
1936 rtFsExtFile_QuerySize,
1937 rtFsExtFile_SetSize,
1938 rtFsExtFile_QueryMaxSize,
1939 RTVFSFILEOPS_VERSION
1940};
1941
1942
1943/**
1944 * Creates a new VFS file from the given regular file inode.
1945 *
1946 * @returns IPRT status code.
1947 * @param pThis The ext volume instance.
1948 * @param fOpen Open flags passed.
1949 * @param iInode The inode for the file.
1950 * @param phVfsFile Where to store the VFS file handle on success.
1951 * @param pErrInfo Where to record additional error information on error, optional.
1952 * @param pszWhat Logging prefix.
1953 */
1954static int rtFsExtVol_NewFile(PRTFSEXTVOL pThis, uint64_t fOpen, uint32_t iInode,
1955 PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo, const char *pszWhat)
1956{
1957 /*
1958 * Load the inode and check that it really is a file.
1959 */
1960 PRTFSEXTINODE pInode = NULL;
1961 int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
1962 if (RT_SUCCESS(rc))
1963 {
1964 if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
1965 {
1966 PRTFSEXTFILE pNewFile;
1967 rc = RTVfsNewFile(&g_rtFsExtFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
1968 phVfsFile, (void **)&pNewFile);
1969 if (RT_SUCCESS(rc))
1970 {
1971 pNewFile->pVol = pThis;
1972 pNewFile->pInode = pInode;
1973 pNewFile->offFile = 0;
1974 }
1975 }
1976 else
1977 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: fMode=%#RX32", pszWhat, pInode->ObjInfo.Attr.fMode);
1978
1979 if (RT_FAILURE(rc))
1980 rtFsExtInodeRelease(pThis, pInode);
1981 }
1982
1983 return rc;
1984}
1985
1986
1987
1988/*
1989 *
1990 * EXT directory code.
1991 * EXT directory code.
1992 * EXT directory code.
1993 *
1994 */
1995
1996/**
1997 * Looks up an entry in the given directory inode.
1998 *
1999 * @returns IPRT status code.
2000 * @param pThis The ext volume instance.
2001 * @param pInode The directory inode structure to.
2002 * @param pszEntry The entry to lookup.
2003 * @param piInode Where to store the inode number if the entry was found.
2004 */
2005static int rtFsExtDir_Lookup(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, const char *pszEntry, uint32_t *piInode)
2006{
2007 uint64_t offEntry = 0;
2008 int rc = VERR_FILE_NOT_FOUND;
2009 uint32_t idxDirEntry = 0;
2010 size_t cchEntry = strlen(pszEntry);
2011
2012 if (cchEntry > 255)
2013 return VERR_FILENAME_TOO_LONG;
2014
2015 while (offEntry < (uint64_t)pInode->ObjInfo.cbObject)
2016 {
2017 EXTDIRENTRYEX DirEntry;
2018 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - offEntry);
2019 int rc2 = rtFsExtInode_Read(pThis, pInode, offEntry, &DirEntry, cbThis, NULL);
2020 if (RT_SUCCESS(rc2))
2021 {
2022#ifdef LOG_ENABLED
2023 rtFsExtDirEntry_Log(pThis, idxDirEntry, &DirEntry);
2024#endif
2025
2026 uint16_t cbName = pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
2027 ? DirEntry.Core.u.v2.cbName
2028 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
2029 if ( cchEntry == cbName
2030 && !memcmp(pszEntry, &DirEntry.Core.achName[0], cchEntry))
2031 {
2032 *piInode = RT_LE2H_U32(DirEntry.Core.iInodeRef);
2033 rc = VINF_SUCCESS;
2034 break;
2035 }
2036
2037 offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
2038 idxDirEntry++;
2039 }
2040 else
2041 {
2042 rc = rc2;
2043 break;
2044 }
2045 }
2046
2047 return rc;
2048}
2049
2050
2051
2052/*
2053 *
2054 * Directory instance methods
2055 * Directory instance methods
2056 * Directory instance methods
2057 *
2058 */
2059
2060/**
2061 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2062 */
2063static DECLCALLBACK(int) rtFsExtDir_Close(void *pvThis)
2064{
2065 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2066 LogFlowFunc(("pThis=%p\n", pThis));
2067 rtFsExtInodeRelease(pThis->pVol, pThis->pInode);
2068 pThis->pInode = NULL;
2069 return VINF_SUCCESS;
2070}
2071
2072
2073/**
2074 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2075 */
2076static DECLCALLBACK(int) rtFsExtDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2077{
2078 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2079 LogFlowFunc(("\n"));
2080 return rtFsExtInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
2081}
2082
2083
2084/**
2085 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2086 */
2087static DECLCALLBACK(int) rtFsExtDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2088{
2089 LogFlowFunc(("\n"));
2090 RT_NOREF(pvThis, fMode, fMask);
2091 return VERR_WRITE_PROTECT;
2092}
2093
2094
2095/**
2096 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2097 */
2098static DECLCALLBACK(int) rtFsExtDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2099 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2100{
2101 LogFlowFunc(("\n"));
2102 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2103 return VERR_WRITE_PROTECT;
2104}
2105
2106
2107/**
2108 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2109 */
2110static DECLCALLBACK(int) rtFsExtDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2111{
2112 LogFlowFunc(("\n"));
2113 RT_NOREF(pvThis, uid, gid);
2114 return VERR_WRITE_PROTECT;
2115}
2116
2117
2118/**
2119 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
2120 */
2121static DECLCALLBACK(int) rtFsExtDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
2122 uint32_t fFlags, PRTVFSOBJ phVfsObj)
2123{
2124 LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
2125 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2126 PRTFSEXTVOL pVol = pThis->pVol;
2127 int rc = VINF_SUCCESS;
2128
2129 RT_NOREF(fFlags);
2130
2131 /*
2132 * We cannot create or replace anything, just open stuff.
2133 */
2134 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
2135 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
2136 { /* likely */ }
2137 else
2138 return VERR_WRITE_PROTECT;
2139
2140 /*
2141 * Lookup the entry.
2142 */
2143 uint32_t iInode = 0;
2144 rc = rtFsExtDir_Lookup(pVol, pThis->pInode, pszEntry, &iInode);
2145 if (RT_SUCCESS(rc))
2146 {
2147 PRTFSEXTINODE pInode = NULL;
2148 rc = rtFsExtInodeLoad(pVol, iInode, &pInode);
2149 if (RT_SUCCESS(rc))
2150 {
2151 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
2152 {
2153 RTVFSDIR hVfsDir;
2154 rc = rtFsExtVol_OpenDirByInode(pVol, iInode, &hVfsDir);
2155 if (RT_SUCCESS(rc))
2156 {
2157 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2158 RTVfsDirRelease(hVfsDir);
2159 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2160 }
2161 }
2162 else if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
2163 {
2164 RTVFSFILE hVfsFile;
2165 rc = rtFsExtVol_NewFile(pVol, fOpen, iInode, &hVfsFile, NULL, pszEntry);
2166 if (RT_SUCCESS(rc))
2167 {
2168 *phVfsObj = RTVfsObjFromFile(hVfsFile);
2169 RTVfsFileRelease(hVfsFile);
2170 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2171 }
2172 }
2173 else
2174 rc = VERR_NOT_SUPPORTED;
2175 }
2176 }
2177
2178 LogFlow(("rtFsExtDir_Open(%s): returns %Rrc\n", pszEntry, rc));
2179 return rc;
2180}
2181
2182
2183/**
2184 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
2185 */
2186static DECLCALLBACK(int) rtFsExtDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
2187{
2188 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
2189 LogFlowFunc(("\n"));
2190 return VERR_WRITE_PROTECT;
2191}
2192
2193
2194/**
2195 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
2196 */
2197static DECLCALLBACK(int) rtFsExtDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
2198{
2199 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
2200 LogFlowFunc(("\n"));
2201 return VERR_NOT_SUPPORTED;
2202}
2203
2204
2205/**
2206 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
2207 */
2208static DECLCALLBACK(int) rtFsExtDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
2209 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
2210{
2211 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
2212 LogFlowFunc(("\n"));
2213 return VERR_WRITE_PROTECT;
2214}
2215
2216
2217/**
2218 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
2219 */
2220static DECLCALLBACK(int) rtFsExtDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
2221{
2222 RT_NOREF(pvThis, pszEntry, fType);
2223 LogFlowFunc(("\n"));
2224 return VERR_WRITE_PROTECT;
2225}
2226
2227
2228/**
2229 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
2230 */
2231static DECLCALLBACK(int) rtFsExtDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
2232{
2233 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
2234 LogFlowFunc(("\n"));
2235 return VERR_WRITE_PROTECT;
2236}
2237
2238
2239/**
2240 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
2241 */
2242static DECLCALLBACK(int) rtFsExtDir_RewindDir(void *pvThis)
2243{
2244 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2245 LogFlowFunc(("\n"));
2246
2247 pThis->fNoMoreFiles = false;
2248 pThis->offEntry = 0;
2249 pThis->idxEntry = 0;
2250 return VINF_SUCCESS;
2251}
2252
2253
2254/**
2255 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
2256 */
2257static DECLCALLBACK(int) rtFsExtDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
2258 RTFSOBJATTRADD enmAddAttr)
2259{
2260 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2261 PRTFSEXTINODE pInode = pThis->pInode;
2262 LogFlowFunc(("\n"));
2263
2264 if (pThis->fNoMoreFiles)
2265 return VERR_NO_MORE_FILES;
2266
2267 EXTDIRENTRYEX DirEntry;
2268 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - pThis->offEntry);
2269 int rc = rtFsExtInode_Read(pThis->pVol, pInode, pThis->offEntry, &DirEntry, cbThis, NULL);
2270 if (RT_SUCCESS(rc))
2271 {
2272#ifdef LOG_ENABLED
2273 rtFsExtDirEntry_Log(pThis->pVol, pThis->idxEntry, &DirEntry);
2274#endif
2275
2276 /* 0 inode entry means unused entry. */
2277 /** @todo Can there be unused entries somewhere in the middle? */
2278 uint32_t iInodeRef = RT_LE2H_U32(DirEntry.Core.iInodeRef);
2279 if (iInodeRef != 0)
2280 {
2281 uint16_t cbName = pThis->pVol->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
2282 ? DirEntry.Core.u.v2.cbName
2283 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
2284
2285 if (cbName <= 255)
2286 {
2287 size_t const cbDirEntry = *pcbDirEntry;
2288
2289 *pcbDirEntry = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cbName + 2]);
2290 if (*pcbDirEntry <= cbDirEntry)
2291 {
2292 /* Load the referenced inode. */
2293 PRTFSEXTINODE pInodeRef;
2294 rc = rtFsExtInodeLoad(pThis->pVol, iInodeRef, &pInodeRef);
2295 if (RT_SUCCESS(rc))
2296 {
2297 memcpy(&pDirEntry->szName[0], &DirEntry.Core.achName[0], cbName);
2298 pDirEntry->szName[cbName] = '\0';
2299 pDirEntry->cbName = cbName;
2300 rc = rtFsExtInode_QueryInfo(pInodeRef, &pDirEntry->Info, enmAddAttr);
2301 if (RT_SUCCESS(rc))
2302 {
2303 pThis->offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
2304 pThis->idxEntry++;
2305 rtFsExtInodeRelease(pThis->pVol, pInodeRef);
2306 return VINF_SUCCESS;
2307 }
2308 rtFsExtInodeRelease(pThis->pVol, pInodeRef);
2309 }
2310 }
2311 else
2312 rc = VERR_BUFFER_OVERFLOW;
2313 }
2314 else
2315 rc = VERR_FILENAME_TOO_LONG;
2316 }
2317 else
2318 {
2319 rc = VERR_NO_MORE_FILES;
2320 LogFlowFunc(("no more files\n"));
2321 pThis->fNoMoreFiles = true;
2322 }
2323 }
2324
2325 return rc;
2326}
2327
2328
2329/**
2330 * EXT directory operations.
2331 */
2332static const RTVFSDIROPS g_rtFsExtDirOps =
2333{
2334 { /* Obj */
2335 RTVFSOBJOPS_VERSION,
2336 RTVFSOBJTYPE_DIR,
2337 "EXT Dir",
2338 rtFsExtDir_Close,
2339 rtFsExtDir_QueryInfo,
2340 RTVFSOBJOPS_VERSION
2341 },
2342 RTVFSDIROPS_VERSION,
2343 0,
2344 { /* ObjSet */
2345 RTVFSOBJSETOPS_VERSION,
2346 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
2347 rtFsExtDir_SetMode,
2348 rtFsExtDir_SetTimes,
2349 rtFsExtDir_SetOwner,
2350 RTVFSOBJSETOPS_VERSION
2351 },
2352 rtFsExtDir_Open,
2353 NULL /* pfnFollowAbsoluteSymlink */,
2354 NULL /* pfnOpenFile */,
2355 NULL /* pfnOpenDir */,
2356 rtFsExtDir_CreateDir,
2357 rtFsExtDir_OpenSymlink,
2358 rtFsExtDir_CreateSymlink,
2359 NULL /* pfnQueryEntryInfo */,
2360 rtFsExtDir_UnlinkEntry,
2361 rtFsExtDir_RenameEntry,
2362 rtFsExtDir_RewindDir,
2363 rtFsExtDir_ReadDir,
2364 RTVFSDIROPS_VERSION,
2365};
2366
2367
2368/**
2369 * Opens a directory by the given inode.
2370 *
2371 * @returns IPRT status code.
2372 * @param pThis The ext volume instance.
2373 * @param iInode The inode to open.
2374 * @param phVfsDir Where to store the handle to the VFS directory on success.
2375 */
2376static int rtFsExtVol_OpenDirByInode(PRTFSEXTVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir)
2377{
2378 PRTFSEXTINODE pInode = NULL;
2379 int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
2380 if (RT_SUCCESS(rc))
2381 {
2382 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
2383 {
2384 PRTFSEXTDIR pNewDir;
2385 rc = RTVfsNewDir(&g_rtFsExtDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
2386 phVfsDir, (void **)&pNewDir);
2387 if (RT_SUCCESS(rc))
2388 {
2389 pNewDir->fNoMoreFiles = false;
2390 pNewDir->pVol = pThis;
2391 pNewDir->pInode = pInode;
2392 }
2393 }
2394 else
2395 rc = VERR_VFS_BOGUS_FORMAT;
2396
2397 if (RT_FAILURE(rc))
2398 rtFsExtInodeRelease(pThis, pInode);
2399 }
2400
2401 return rc;
2402}
2403
2404
2405
2406/*
2407 *
2408 * Volume level code.
2409 * Volume level code.
2410 * Volume level code.
2411 *
2412 */
2413
2414/**
2415 * Checks whether the block range in the given block group is in use by checking the
2416 * block bitmap.
2417 *
2418 * @returns Flag whether the range is in use.
2419 * @param pBlkGrpDesc The block group to check for.
2420 * @param iBlockStart The starting block to check relative from the beginning of the block group.
2421 * @param cBlocks How many blocks to check.
2422 */
2423static bool rtFsExtIsBlockRangeInUse(PRTFSEXTBLKGRP pBlkGrpDesc, uint64_t iBlockStart, size_t cBlocks)
2424{
2425 /** @todo Optimize with ASMBitFirstSet(). */
2426 while (cBlocks)
2427 {
2428 uint32_t idxByte = iBlockStart / 8;
2429 uint32_t iBit = iBlockStart % 8;
2430
2431 if (pBlkGrpDesc->abBlockBitmap[idxByte] & RT_BIT(iBit))
2432 return true;
2433
2434 cBlocks--;
2435 iBlockStart++;
2436 }
2437
2438 return false;
2439}
2440
2441
2442static DECLCALLBACK(int) rtFsExtVolBlockGroupTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
2443{
2444 RT_NOREF(pvUser);
2445
2446 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)pCore;
2447 Assert(!pBlockGroup->cRefs);
2448 RTMemFree(pBlockGroup);
2449 return VINF_SUCCESS;
2450}
2451
2452
2453static DECLCALLBACK(int) rtFsExtVolInodeTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
2454{
2455 RT_NOREF(pvUser);
2456
2457 PRTFSEXTINODE pInode = (PRTFSEXTINODE)pCore;
2458 Assert(!pInode->cRefs);
2459 RTMemFree(pInode);
2460 return VINF_SUCCESS;
2461}
2462
2463
2464static DECLCALLBACK(int) rtFsExtVolBlockTreeDestroy(PAVLU64NODECORE pCore, void *pvUser)
2465{
2466 RT_NOREF(pvUser);
2467
2468 PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)pCore;
2469 Assert(!pBlock->cRefs);
2470 RTMemFree(pBlock);
2471 return VINF_SUCCESS;
2472}
2473
2474
2475/**
2476 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
2477 */
2478static DECLCALLBACK(int) rtFsExtVol_Close(void *pvThis)
2479{
2480 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2481
2482 /* Destroy the block group tree. */
2483 RTAvlU32Destroy(&pThis->BlockGroupRoot, rtFsExtVolBlockGroupTreeDestroy, pThis);
2484 pThis->BlockGroupRoot = NULL;
2485 RTListInit(&pThis->LstBlockGroupLru);
2486
2487 /* Destroy the inode tree. */
2488 RTAvlU32Destroy(&pThis->InodeRoot, rtFsExtVolInodeTreeDestroy, pThis);
2489 pThis->InodeRoot = NULL;
2490 RTListInit(&pThis->LstInodeLru);
2491
2492 /* Destroy the block cache tree. */
2493 RTAvlU64Destroy(&pThis->BlockRoot, rtFsExtVolBlockTreeDestroy, pThis);
2494 pThis->BlockRoot = NULL;
2495 RTListInit(&pThis->LstBlockLru);
2496
2497 /*
2498 * Backing file and handles.
2499 */
2500 RTVfsFileRelease(pThis->hVfsBacking);
2501 pThis->hVfsBacking = NIL_RTVFSFILE;
2502 pThis->hVfsSelf = NIL_RTVFS;
2503
2504 return VINF_SUCCESS;
2505}
2506
2507
2508/**
2509 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
2510 */
2511static DECLCALLBACK(int) rtFsExtVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2512{
2513 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
2514 return VERR_WRONG_TYPE;
2515}
2516
2517
2518/**
2519 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
2520 */
2521static DECLCALLBACK(int) rtFsExtVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
2522{
2523 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2524 int rc = rtFsExtVol_OpenDirByInode(pThis, EXT_INODE_NR_ROOT_DIR, phVfsDir);
2525 LogFlowFunc(("returns %Rrc\n", rc));
2526 return rc;
2527}
2528
2529
2530/**
2531 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
2532 */
2533static DECLCALLBACK(int) rtFsExtVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
2534{
2535 int rc = VINF_SUCCESS;
2536 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2537
2538 *pfUsed = false;
2539
2540 uint64_t iBlock = rtFsExtDiskOffsetToBlockIdx(pThis, off);
2541 uint64_t cBlocks = rtFsExtDiskOffsetToBlockIdx(pThis, cb) + (cb % pThis->cbBlock ? 1 : 0);
2542 while (cBlocks > 0)
2543 {
2544 uint32_t const iBlockGroup = iBlock / pThis->cBlocksPerGroup;
2545 uint32_t const iBlockRelStart = iBlock - iBlockGroup * pThis->cBlocksPerGroup;
2546 PRTFSEXTBLKGRP pBlockGroup = NULL;
2547
2548 rc = rtFsExtBlockGroupLoad(pThis, iBlockGroup, &pBlockGroup);
2549 if (RT_FAILURE(rc))
2550 break;
2551
2552 uint64_t cBlocksThis = RT_MIN(cBlocks, iBlockRelStart - pThis->cBlocksPerGroup);
2553 if (rtFsExtIsBlockRangeInUse(pBlockGroup, iBlockRelStart, cBlocksThis))
2554 {
2555 *pfUsed = true;
2556 break;
2557 }
2558
2559 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
2560 cBlocks -= cBlocksThis;
2561 iBlock += cBlocksThis;
2562 }
2563
2564 return rc;
2565}
2566
2567
2568DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsExtVolOps =
2569{
2570 /* .Obj = */
2571 {
2572 /* .uVersion = */ RTVFSOBJOPS_VERSION,
2573 /* .enmType = */ RTVFSOBJTYPE_VFS,
2574 /* .pszName = */ "ExtVol",
2575 /* .pfnClose = */ rtFsExtVol_Close,
2576 /* .pfnQueryInfo = */ rtFsExtVol_QueryInfo,
2577 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
2578 },
2579 /* .uVersion = */ RTVFSOPS_VERSION,
2580 /* .fFeatures = */ 0,
2581 /* .pfnOpenRoot = */ rtFsExtVol_OpenRoot,
2582 /* .pfnQueryRangeState = */ rtFsExtVol_QueryRangeState,
2583 /* .uEndMarker = */ RTVFSOPS_VERSION
2584};
2585
2586
2587
2588/**
2589 * Loads the parameters from the given original ext2 format superblock (EXT_SB_REV_ORIG).
2590 *
2591 * @returns IPRT status code.
2592 * @param pThis The ext volume instance.
2593 * @param pSb The superblock to load.
2594 * @param pErrInfo Where to return additional error info.
2595 */
2596static int rtFsExtVolLoadAndParseSuperBlockV0(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
2597{
2598 RT_NOREF(pErrInfo);
2599
2600 /*
2601 * Linux never supported a differing cluster (also called fragment) size for
2602 * the original ext2 layout so we reject such filesystems as it is not clear what
2603 * the purpose is really.
2604 */
2605 if (RT_LE2H_U32(pSb->cLogBlockSize) != RT_LE2H_U32(pSb->cLogClusterSize))
2606 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem cluster and block size differ");
2607
2608 pThis->f64Bit = false;
2609 pThis->cBlockShift = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
2610 pThis->cbBlock = UINT64_C(1) << pThis->cBlockShift;
2611 pThis->cbInode = sizeof(EXTINODE);
2612 pThis->cbBlkGrpDesc = sizeof(EXTBLOCKGROUPDESC32);
2613 pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
2614 pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
2615 pThis->cBlockGroups = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
2616 pThis->cbBlockBitmap = pThis->cBlocksPerGroup / 8;
2617 if (pThis->cBlocksPerGroup % 8)
2618 pThis->cbBlockBitmap++;
2619 pThis->cbInodeBitmap = pThis->cInodesPerGroup / 8;
2620 if (pThis->cInodesPerGroup % 8)
2621 pThis->cbInodeBitmap++;
2622
2623 return VINF_SUCCESS;
2624}
2625
2626
2627/**
2628 * Loads the parameters from the given ext superblock (EXT_SB_REV_V2_DYN_INODE_SZ).
2629 *
2630 * @returns IPRT status code.
2631 * @param pThis The ext volume instance.
2632 * @param pSb The superblock to load.
2633 * @param pErrInfo Where to return additional error info.
2634 */
2635static int rtFsExtVolLoadAndParseSuperBlockV1(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
2636{
2637 if ((RT_LE2H_U32(pSb->fFeaturesIncompat) & ~RTFSEXT_INCOMPAT_FEATURES_SUPP) != 0)
2638 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported incompatible features: %RX32",
2639 RT_LE2H_U32(pSb->fFeaturesIncompat) & ~RTFSEXT_INCOMPAT_FEATURES_SUPP);
2640 if ( RT_LE2H_U32(pSb->fFeaturesCompatRo) != 0
2641 && !(pThis->fMntFlags & RTVFSMNT_F_READ_ONLY))
2642 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported readonly features: %RX32",
2643 RT_LE2H_U32(pSb->fFeaturesCompatRo));
2644
2645 pThis->fFeaturesIncompat = RT_LE2H_U32(pSb->fFeaturesIncompat);
2646 pThis->f64Bit = RT_BOOL(pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_64BIT);
2647 pThis->cBlockShift = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
2648 pThis->cbBlock = UINT64_C(1) << pThis->cBlockShift;
2649 pThis->cbInode = RT_LE2H_U16(pSb->cbInode);
2650 pThis->cbBlkGrpDesc = pThis->f64Bit ? RT_LE2H_U16(pSb->cbGroupDesc) : sizeof(EXTBLOCKGROUPDESC32);
2651 pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
2652 pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
2653 pThis->cBlockGroups = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
2654 pThis->cbBlockBitmap = pThis->cBlocksPerGroup / 8;
2655 if (pThis->cBlocksPerGroup % 8)
2656 pThis->cbBlockBitmap++;
2657 pThis->cbInodeBitmap = pThis->cInodesPerGroup / 8;
2658 if (pThis->cInodesPerGroup % 8)
2659 pThis->cbInodeBitmap++;
2660
2661 return VINF_SUCCESS;
2662}
2663
2664
2665/**
2666 * Loads and parses the superblock of the filesystem.
2667 *
2668 * @returns IPRT status code.
2669 * @param pThis The ext volume instance.
2670 * @param pErrInfo Where to return additional error info.
2671 */
2672static int rtFsExtVolLoadAndParseSuperblock(PRTFSEXTVOL pThis, PRTERRINFO pErrInfo)
2673{
2674 int rc = VINF_SUCCESS;
2675 EXTSUPERBLOCK Sb;
2676 rc = RTVfsFileReadAt(pThis->hVfsBacking, EXT_SB_OFFSET, &Sb, sizeof(EXTSUPERBLOCK), NULL);
2677 if (RT_FAILURE(rc))
2678 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
2679
2680 /* Validate the superblock. */
2681 if (RT_LE2H_U16(Sb.u16Signature) != EXT_SB_SIGNATURE)
2682 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not EXT - Signature mismatch: %RX16", RT_LE2H_U16(Sb.u16Signature));
2683
2684#ifdef LOG_ENABLED
2685 rtFsExtSb_Log(&Sb);
2686#endif
2687
2688 if (RT_LE2H_U16(Sb.u16FilesystemState) == EXT_SB_STATE_ERRORS)
2689 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains errors");
2690
2691 if (RT_LE2H_U32(Sb.u32RevLvl) == EXT_SB_REV_ORIG)
2692 rc = rtFsExtVolLoadAndParseSuperBlockV0(pThis, &Sb, pErrInfo);
2693 else
2694 rc = rtFsExtVolLoadAndParseSuperBlockV1(pThis, &Sb, pErrInfo);
2695
2696 return rc;
2697}
2698
2699
2700RTDECL(int) RTFsExtVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fExtFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
2701{
2702 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
2703 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
2704 AssertReturn(!fExtFlags, VERR_INVALID_FLAGS);
2705
2706 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
2707 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2708
2709 /*
2710 * Create a VFS instance and initialize the data so rtFsExtVol_Close works.
2711 */
2712 RTVFS hVfs;
2713 PRTFSEXTVOL pThis;
2714 int rc = RTVfsNew(&g_rtFsExtVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
2715 if (RT_SUCCESS(rc))
2716 {
2717 pThis->hVfsBacking = hVfsFileIn;
2718 pThis->hVfsSelf = hVfs;
2719 pThis->fMntFlags = fMntFlags;
2720 pThis->fExtFlags = fExtFlags;
2721 pThis->BlockGroupRoot = NULL;
2722 pThis->InodeRoot = NULL;
2723 pThis->BlockRoot = NULL;
2724 pThis->cbBlockGroups = 0;
2725 pThis->cbInodes = 0;
2726 pThis->cbBlocks = 0;
2727 RTListInit(&pThis->LstBlockGroupLru);
2728 RTListInit(&pThis->LstInodeLru);
2729 RTListInit(&pThis->LstBlockLru);
2730
2731 rc = RTVfsFileQuerySize(pThis->hVfsBacking, &pThis->cbBacking);
2732 if (RT_SUCCESS(rc))
2733 {
2734 rc = rtFsExtVolLoadAndParseSuperblock(pThis, pErrInfo);
2735 if (RT_SUCCESS(rc))
2736 {
2737 *phVfs = hVfs;
2738 return VINF_SUCCESS;
2739 }
2740 }
2741
2742 RTVfsRelease(hVfs);
2743 *phVfs = NIL_RTVFS;
2744 }
2745 else
2746 RTVfsFileRelease(hVfsFileIn);
2747
2748 return rc;
2749}
2750
2751
2752/**
2753 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
2754 */
2755static DECLCALLBACK(int) rtVfsChainExtVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
2756 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
2757{
2758 RT_NOREF(pProviderReg);
2759
2760 /*
2761 * Basic checks.
2762 */
2763 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
2764 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
2765 if ( pElement->enmType != RTVFSOBJTYPE_VFS
2766 && pElement->enmType != RTVFSOBJTYPE_DIR)
2767 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
2768 if (pElement->cArgs > 1)
2769 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
2770
2771 /*
2772 * Parse the flag if present, save in pElement->uProvider.
2773 */
2774 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
2775 if (pElement->cArgs > 0)
2776 {
2777 const char *psz = pElement->paArgs[0].psz;
2778 if (*psz)
2779 {
2780 if (!strcmp(psz, "ro"))
2781 fReadOnly = true;
2782 else if (!strcmp(psz, "rw"))
2783 fReadOnly = false;
2784 else
2785 {
2786 *poffError = pElement->paArgs[0].offSpec;
2787 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
2788 }
2789 }
2790 }
2791
2792 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
2793 return VINF_SUCCESS;
2794}
2795
2796
2797/**
2798 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2799 */
2800static DECLCALLBACK(int) rtVfsChainExtVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2801 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2802 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2803{
2804 RT_NOREF(pProviderReg, pSpec, poffError);
2805
2806 int rc;
2807 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2808 if (hVfsFileIn != NIL_RTVFSFILE)
2809 {
2810 RTVFS hVfs;
2811 rc = RTFsExtVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
2812 RTVfsFileRelease(hVfsFileIn);
2813 if (RT_SUCCESS(rc))
2814 {
2815 *phVfsObj = RTVfsObjFromVfs(hVfs);
2816 RTVfsRelease(hVfs);
2817 if (*phVfsObj != NIL_RTVFSOBJ)
2818 return VINF_SUCCESS;
2819 rc = VERR_VFS_CHAIN_CAST_FAILED;
2820 }
2821 }
2822 else
2823 rc = VERR_VFS_CHAIN_CAST_FAILED;
2824 return rc;
2825}
2826
2827
2828/**
2829 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2830 */
2831static DECLCALLBACK(bool) rtVfsChainExtVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2832 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2833 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2834{
2835 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2836 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2837 || !pReuseElement->paArgs[0].uProvider)
2838 return true;
2839 return false;
2840}
2841
2842
2843/** VFS chain element 'ext'. */
2844static RTVFSCHAINELEMENTREG g_rtVfsChainExtVolReg =
2845{
2846 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2847 /* fReserved = */ 0,
2848 /* pszName = */ "ext",
2849 /* ListEntry = */ { NULL, NULL },
2850 /* pszHelp = */ "Open a EXT file system, requires a file object on the left side.\n"
2851 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
2852 /* pfnValidate = */ rtVfsChainExtVol_Validate,
2853 /* pfnInstantiate = */ rtVfsChainExtVol_Instantiate,
2854 /* pfnCanReuseElement = */ rtVfsChainExtVol_CanReuseElement,
2855 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2856};
2857
2858RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainExtVolReg, rtVfsChainExtVolReg);
2859
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