VirtualBox

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

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

Runtime/fs/extvfs: Working on parsing block group and inode descriptors [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.3 KB
Line 
1/* $Id: extvfs.cpp 76294 2018-12-19 14:00:23Z vboxsync $ */
2/** @file
3 * IPRT - Ext2/3/4 Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2012-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include <iprt/fsvfs.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/avl.h>
37#include <iprt/file.h>
38#include <iprt/list.h>
39#include <iprt/log.h>
40#include <iprt/mem.h>
41#include <iprt/string.h>
42#include <iprt/vfs.h>
43#include <iprt/vfslowlevel.h>
44#include <iprt/formats/ext.h>
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** The maximum block group cache size (in bytes). */
51#if ARCH_BITS >= 64
52# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _512K
53#else
54# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _128K
55#endif
56/** The maximum inode cache size (in bytes). */
57#if ARCH_BITS >= 64
58# define RTFSEXT_MAX_INODE_CACHE_SIZE _512K
59#else
60# define RTFSEXT_MAX_INODE_CACHE_SIZE _128K
61#endif
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67/** Pointer to the ext filesystem data. */
68typedef struct RTFSEXTVOL *PRTFSEXTVOL;
69
70
71/**
72 * Cached block group descriptor data.
73 */
74typedef struct RTFSEXTBLKGRP
75{
76 /** AVL tree node, indexed by the block group number. */
77 AVLU32NODECORE Core;
78 /** List node for the LRU list used for eviction. */
79 RTLISTNODE NdLru;
80 /** Reference counter. */
81 volatile uint32_t cRefs;
82 /** Block number where the inode table is store. */
83 uint64_t iBlockInodeTbl;
84 /** Pointer to the inode bitmap. */
85 uint8_t *pabInodeBitmap;
86 /** Block bitmap - variable in size (depends on the block size
87 * and number of blocks per group). */
88 uint8_t abBlockBitmap[1];
89} RTFSEXTBLKGRP;
90/** Pointer to block group descriptor data. */
91typedef RTFSEXTBLKGRP *PRTFSEXTBLKGRP;
92
93
94/**
95 * In-memory inode.
96 */
97typedef struct RTFSEXTINODE
98{
99 /** AVL tree node, indexed by the inode number. */
100 AVLU32NODECORE Core;
101 /** List node for the inode LRU list used for eviction. */
102 RTLISTNODE NdLru;
103 /** Reference counter. */
104 volatile uint32_t cRefs;
105 /** Block number where the inode is stored. */
106 uint64_t iBlockInode;
107 /** Offset in bytes into block where the inode is stored. */
108 uint32_t offInode;
109 /** Inode data size. */
110 uint64_t cbData;
111} RTFSEXTINODE;
112/** Pointer to an in-memory inode. */
113typedef RTFSEXTINODE *PRTFSEXTINODE;
114
115
116/**
117 * Open directory instance.
118 */
119typedef struct RTFSEXTDIR
120{
121 /** Volume this directory belongs to. */
122 PRTFSEXTVOL pVol;
123 /** Set if we've reached the end of the directory enumeration. */
124 bool fNoMoreFiles;
125} RTFSEXTDIR;
126/** Pointer to an open directory instance. */
127typedef RTFSEXTDIR *PRTFSEXTDIR;
128
129
130/**
131 * Ext2/3/4 filesystem volume.
132 */
133typedef struct RTFSEXTVOL
134{
135 /** Handle to itself. */
136 RTVFS hVfsSelf;
137 /** The file, partition, or whatever backing the ext volume. */
138 RTVFSFILE hVfsBacking;
139 /** The size of the backing thingy. */
140 uint64_t cbBacking;
141
142 /** RTVFSMNT_F_XXX. */
143 uint32_t fMntFlags;
144 /** RTFSEXTVFS_F_XXX (currently none defined). */
145 uint32_t fExtFlags;
146
147 /** Flag whether the filesystem is 64bit. */
148 bool f64Bit;
149 /** Size of one block. */
150 size_t cbBlock;
151 /** Number of bits to shift left for fast conversion of block numbers to offsets. */
152 uint32_t cBlockShift;
153 /** Number of blocks in one group. */
154 uint32_t cBlocksPerGroup;
155 /** Number of inodes in each block group. */
156 uint32_t cInodesPerGroup;
157 /** Number of blocks groups in the volume. */
158 uint32_t cBlockGroups;
159 /** Size of the block bitmap. */
160 size_t cbBlockBitmap;
161 /** Size of the inode bitmap. */
162 size_t cbInodeBitmap;
163 /** Size of block group descriptor. */
164 size_t cbBlkGrpDesc;
165 /** Size of an inode. */
166 size_t cbInode;
167
168 /** @name Block group cache.
169 * @{ */
170 /** LRU list anchor. */
171 RTLISTANCHOR LstBlockGroupLru;
172 /** Root of the cached block group tree. */
173 AVLU32TREE BlockGroupRoot;
174 /** Size of the cached block groups. */
175 size_t cbBlockGroups;
176 /** @} */
177
178 /** @name Inode cache.
179 * @{ */
180 /** LRU list anchor for the inode cache. */
181 RTLISTANCHOR LstInodeLru;
182 /** Root of the cached inode tree. */
183 AVLU32TREE InodeRoot;
184 /** Size of the cached inodes. */
185 size_t cbInodes;
186 /** @} */
187} RTFSEXTVOL;
188
189
190
191/*********************************************************************************************************************************
192* Internal Functions *
193*********************************************************************************************************************************/
194
195#ifdef LOG_ENABLED
196/**
197 * Logs the ext filesystem superblock.
198 *
199 * @returns nothing.
200 * @param pSb Pointer to the superblock.
201 */
202static void rtFsExtSb_Log(PCEXTSUPERBLOCK pSb)
203{
204 if (LogIs2Enabled())
205 {
206 RTTIMESPEC Spec;
207 char sz[80];
208
209 Log2(("EXT: Superblock:\n"));
210 Log2(("EXT: cInodesTotal %RU32\n", RT_LE2H_U32(pSb->cInodesTotal)));
211 Log2(("EXT: cBlocksTotalLow %RU32\n", RT_LE2H_U32(pSb->cBlocksTotalLow)));
212 Log2(("EXT: cBlocksRsvdForSuperUserLow %RU32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserLow)));
213 Log2(("EXT: cBlocksFreeLow %RU32\n", RT_LE2H_U32(pSb->cBlocksFreeLow)));
214 Log2(("EXT: cInodesFree %RU32\n", RT_LE2H_U32(pSb->cInodesFree)));
215 Log2(("EXT: iBlockOfSuperblock %RU32\n", RT_LE2H_U32(pSb->iBlockOfSuperblock)));
216 Log2(("EXT: cLogBlockSize %RU32\n", RT_LE2H_U32(pSb->cLogBlockSize)));
217 Log2(("EXT: cLogClusterSize %RU32\n", RT_LE2H_U32(pSb->cLogClusterSize)));
218 Log2(("EXT: cBlocksPerGroup %RU32\n", RT_LE2H_U32(pSb->cBlocksPerGroup)));
219 Log2(("EXT: cClustersPerBlockGroup %RU32\n", RT_LE2H_U32(pSb->cClustersPerBlockGroup)));
220 Log2(("EXT: cInodesPerBlockGroup %RU32\n", RT_LE2H_U32(pSb->cInodesPerBlockGroup)));
221 Log2(("EXT: u32LastMountTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastMountTime),
222 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastMountTime)), sz, sizeof(sz))));
223 Log2(("EXT: u32LastWrittenTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastWrittenTime),
224 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastWrittenTime)), sz, sizeof(sz))));
225 Log2(("EXT: cMountsSinceLastCheck %RU16\n", RT_LE2H_U32(pSb->cMountsSinceLastCheck)));
226 Log2(("EXT: cMaxMountsUntilCheck %RU16\n", RT_LE2H_U32(pSb->cMaxMountsUntilCheck)));
227 Log2(("EXT: u16Signature %#RX16\n", RT_LE2H_U32(pSb->u16Signature)));
228 Log2(("EXT: u16FilesystemState %#RX16\n", RT_LE2H_U32(pSb->u16FilesystemState)));
229 Log2(("EXT: u16ActionOnError %#RX16\n", RT_LE2H_U32(pSb->u16ActionOnError)));
230 Log2(("EXT: u16RevLvlMinor %#RX16\n", RT_LE2H_U32(pSb->u16RevLvlMinor)));
231 Log2(("EXT: u32LastCheckTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastCheckTime),
232 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastCheckTime)), sz, sizeof(sz))));
233 Log2(("EXT: u32CheckInterval %RU32\n", RT_LE2H_U32(pSb->u32CheckInterval)));
234 Log2(("EXT: u32OsIdCreator %#RX32\n", RT_LE2H_U32(pSb->u32OsIdCreator)));
235 Log2(("EXT: u32RevLvl %#RX32\n", RT_LE2H_U32(pSb->u32RevLvl)));
236 Log2(("EXT: u16UidReservedBlocks %#RX16\n", RT_LE2H_U32(pSb->u16UidReservedBlocks)));
237 Log2(("EXT: u16GidReservedBlocks %#RX16\n", RT_LE2H_U32(pSb->u16GidReservedBlocks)));
238 if (RT_LE2H_U32(pSb->u32RevLvl) == EXT_SB_REV_V2_DYN_INODE_SZ)
239 {
240 Log2(("EXT: iFirstInodeNonRsvd %#RX32\n", RT_LE2H_U32(pSb->iFirstInodeNonRsvd)));
241 Log2(("EXT: cbInode %#RX16\n", RT_LE2H_U32(pSb->cbInode)));
242 Log2(("EXT: iBlkGrpSb %#RX16\n", RT_LE2H_U32(pSb->iBlkGrpSb)));
243 Log2(("EXT: fFeaturesCompat %#RX32%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompat),
244 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_PREALLOC ? " dir-prealloc" : "",
245 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_IMAGIC_INODES ? " imagic-inode" : "",
246 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_HAS_JOURNAL ? " has-journal" : "",
247 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXT_ATTR ? " ext-attrs" : "",
248 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_RESIZE_INODE ? " resize-inode" : "",
249 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_INDEX ? " dir-index" : "",
250 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_LAZY_BG ? " lazy-bg" : "",
251 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_INODE ? " excl-inode" : "",
252 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_BITMAP ? " excl-bitmap" : "",
253 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_SPARSE_SUPER2 ? " sparse-super2" : ""));
254 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),
255 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_COMPRESSION ? " compression" : "",
256 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE ? " dir-filetype" : "",
257 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_RECOVER ? " recovery" : "",
258 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_JOURNAL_DEV ? " journal-dev" : "",
259 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_META_BG ? " meta-bg" : "",
260 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXTENTS ? " extents" : "",
261 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_64BIT ? " 64bit" : "",
262 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_MMP ? " mmp" : "",
263 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_FLEX_BG ? " flex-bg" : "",
264 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXT_ATTR_INODE ? " extattr-inode" : "",
265 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIRDATA ? " dir-data" : "",
266 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_CSUM_SEED ? " csum-seed" : "",
267 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_LARGE_DIR ? " large-dir" : "",
268 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_INLINE_DATA ? " inline-data" : "",
269 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_ENCRYPT ? " encrypt" : ""));
270 Log2(("EXT: fFeaturesCompatRo %#RX32%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompatRo),
271 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_SPARSE_SUPER ? " sparse-super" : "",
272 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_LARGE_FILE ? " large-file" : "",
273 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BTREE_DIR ? " btree-dir" : "",
274 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HUGE_FILE ? " huge-file" : "",
275 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_GDT_CHSKUM ? " gdt-chksum" : "",
276 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_DIR_NLINK ? " dir-nlink" : "",
277 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_EXTRA_INODE_SZ ? " extra-inode" : "",
278 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HAS_SNAPSHOTS ? " snapshots" : "",
279 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_QUOTA ? " quota" : "",
280 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BIGALLOC ? " big-alloc" : "",
281 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_METADATA_CHKSUM ? " meta-chksum" : "",
282 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_REPLICA ? " replica" : "",
283 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_READONLY ? " ro" : "",
284 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_PROJECT ? " project" : ""));
285 Log2(("EXT: au8Uuid <todo>\n"));
286 Log2(("EXT: achVolumeName %16s\n", &pSb->achVolumeName[0]));
287 Log2(("EXT: achLastMounted %64s\n", &pSb->achLastMounted[0]));
288 Log2(("EXT: u32AlgoUsageBitmap %#RX32\n", RT_LE2H_U32(pSb->u32AlgoUsageBitmap)));
289 Log2(("EXT: cBlocksPrealloc %RU8\n", pSb->cBlocksPrealloc));
290 Log2(("EXT: cBlocksPreallocDirectory %RU8\n", pSb->cBlocksPreallocDirectory));
291 Log2(("EXT: cGdtEntriesRsvd %RU16\n", pSb->cGdtEntriesRsvd));
292 Log2(("EXT: au8JournalUuid <todo>\n"));
293 Log2(("EXT: iJournalInode %#RX32\n", RT_LE2H_U32(pSb->iJournalInode)));
294 Log2(("EXT: u32JournalDev %#RX32\n", RT_LE2H_U32(pSb->u32JournalDev)));
295 Log2(("EXT: u32LastOrphan %#RX32\n", RT_LE2H_U32(pSb->u32LastOrphan)));
296 Log2(("EXT: au32HashSeedHtree[0] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[0])));
297 Log2(("EXT: au32HashSeedHtree[1] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[1])));
298 Log2(("EXT: au32HashSeedHtree[2] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[2])));
299 Log2(("EXT: au32HashSeedHtree[3] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[3])));
300 Log2(("EXT: u8HashVersionDef %#RX8\n", pSb->u8HashVersionDef));
301 Log2(("EXT: u8JnlBackupType %#RX8\n", pSb->u8JnlBackupType));
302 Log2(("EXT: cbGroupDesc %RU16\n", RT_LE2H_U16(pSb->cbGroupDesc)));
303 Log2(("EXT: fMntOptsDef %#RX32\n", RT_LE2H_U32(pSb->fMntOptsDef)));
304 Log2(("EXT: iFirstMetaBg %#RX32\n", RT_LE2H_U32(pSb->iFirstMetaBg)));
305 Log2(("EXT: u32TimeFsCreation %#RX32 %s\n", RT_LE2H_U32(pSb->u32TimeFsCreation),
306 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32TimeFsCreation)), sz, sizeof(sz))));
307 for (unsigned i = 0; i < RT_ELEMENTS(pSb->au32JnlBlocks); i++)
308 Log2(("EXT: au32JnlBlocks[%u] %#RX32\n", i, RT_LE2H_U32(pSb->au32JnlBlocks[i])));
309 Log2(("EXT: cBlocksTotalHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksTotalHigh)));
310 Log2(("EXT: cBlocksRsvdForSuperUserHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserHigh)));
311 Log2(("EXT: cBlocksFreeHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksFreeHigh)));
312 Log2(("EXT: cbInodesExtraMin %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
313 Log2(("EXT: cbNewInodesRsv %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
314 Log2(("EXT: fFlags %#RX32\n", RT_LE2H_U32(pSb->fFlags)));
315 Log2(("EXT: cRaidStride %RU16\n", RT_LE2H_U16(pSb->cRaidStride)));
316 Log2(("EXT: cSecMmpInterval %RU16\n", RT_LE2H_U16(pSb->cSecMmpInterval)));
317 Log2(("EXT: iMmpBlock %#RX64\n", RT_LE2H_U64(pSb->iMmpBlock)));
318 Log2(("EXT: cRaidStrideWidth %#RX32\n", RT_LE2H_U32(pSb->cRaidStrideWidth)));
319 Log2(("EXT: cLogGroupsPerFlex %RU8\n", pSb->cLogGroupsPerFlex));
320 Log2(("EXT: u8ChksumType %RX8\n", pSb->u8ChksumType));
321 Log2(("EXT: cKbWritten %#RX64\n", RT_LE2H_U64(pSb->cKbWritten)));
322 Log2(("EXT: iSnapshotInode %#RX32\n", RT_LE2H_U32(pSb->iSnapshotInode)));
323 Log2(("EXT: iSnapshotId %#RX32\n", RT_LE2H_U32(pSb->iSnapshotId)));
324 Log2(("EXT: cSnapshotRsvdBlocks %#RX64\n", RT_LE2H_U64(pSb->cSnapshotRsvdBlocks)));
325 Log2(("EXT: iSnapshotListInode %#RX32\n", RT_LE2H_U32(pSb->iSnapshotListInode)));
326 Log2(("EXT: cErrorsSeen %#RX32\n", RT_LE2H_U32(pSb->cErrorsSeen)));
327 Log2(("EXT: [...]\n")); /** @todo: Missing fields if becoming interesting. */
328 Log2(("EXT: iInodeLostFound %#RX32\n", RT_LE2H_U32(pSb->iInodeLostFound)));
329 Log2(("EXT: iInodeProjQuota %#RX32\n", RT_LE2H_U32(pSb->iInodeProjQuota)));
330 Log2(("EXT: u32ChksumSeed %#RX32\n", RT_LE2H_U32(pSb->u32ChksumSeed)));
331 Log2(("EXT: [...]\n")); /** @todo: Missing fields if becoming interesting. */
332 Log2(("EXT: u32Chksum %#RX32\n", RT_LE2H_U32(pSb->u32Chksum)));
333 }
334 }
335}
336
337
338/**
339 * Logs a ext filesystem block group descriptor.
340 *
341 * @returns nothing.
342 * @param pThis The ext volume instance.
343 * @param iBlockGroup Block group number.
344 * @param pBlockGroup Pointer to the block group.
345 */
346static void rtFsExtBlockGroup_Log(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PCEXTBLOCKGROUPDESC pBlockGroup)
347{
348 if (LogIs2Enabled())
349 {
350 uint64_t iBlockStart = (uint64_t)iBlockGroup * pThis->cBlocksPerGroup;
351 Log2(("EXT: Block group %#RX32 (blocks %#RX64 to %#RX64):\n",
352 iBlockGroup, iBlockStart, iBlockStart + pThis->cBlocksPerGroup - 1));
353 Log2(("EXT: offBlockBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offBlockBitmapLow)));
354 Log2(("EXT: offInodeBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeBitmapLow)));
355 Log2(("EXT: offInodeTableLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeTableLow)));
356 Log2(("EXT: cBlocksFreeLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cBlocksFreeLow)));
357 Log2(("EXT: cInodesFreeLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodesFreeLow)));
358 Log2(("EXT: cDirectoriesLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cDirectoriesLow)));
359 Log2(("EXT: fFlags %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.fFlags)));
360 Log2(("EXT: offSnapshotExclBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offSnapshotExclBitmapLow)));
361 Log2(("EXT: u16ChksumBlockBitmapLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumBlockBitmapLow)));
362 Log2(("EXT: u16ChksumInodeBitmapLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumInodeBitmapLow)));
363 Log2(("EXT: cInodeTblUnusedLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodeTblUnusedLow)));
364 Log2(("EXT: u16Chksum %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16Chksum)));
365 if (pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
366 {
367 Log2(("EXT: offBlockBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offBlockBitmapHigh)));
368 Log2(("EXT: offInodeBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeBitmapHigh)));
369 Log2(("EXT: offInodeTableHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeTableHigh)));
370 Log2(("EXT: cBlocksFreeHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cBlocksFreeHigh)));
371 Log2(("EXT: cInodesFreeHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodesFreeHigh)));
372 Log2(("EXT: cDirectoriesHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cDirectoriesHigh)));
373 Log2(("EXT: cInodeTblUnusedHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodeTblUnusedHigh)));
374 Log2(("EXT: offSnapshotExclBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offSnapshotExclBitmapHigh)));
375 Log2(("EXT: u16ChksumBlockBitmapHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumBlockBitmapHigh)));
376 Log2(("EXT: u16ChksumInodeBitmapHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumInodeBitmapHigh)));
377 }
378 }
379}
380
381
382/**
383 * Logs a ext filesystem inode.
384 *
385 * @returns nothing.
386 * @param pThis The ext volume instance.
387 * @param iInode Inode number.
388 * @param pInode Pointer to the inode.
389 */
390static void rtFsExtInode_Log(PRTFSEXTVOL pThis, uint32_t iInode, PCEXTINODECOMB pInode)
391{
392 if (LogIs2Enabled())
393 {
394 RTTIMESPEC Spec;
395 char sz[80];
396
397 Log2(("EXT: Inode %#RX32:\n", iInode));
398 Log2(("EXT: fMode %#RX16\n", RT_LE2H_U16(pInode->Core.fMode)));
399 Log2(("EXT: uUidLow %#RX16\n", RT_LE2H_U16(pInode->Core.uUidLow)));
400 Log2(("EXT: cbSizeLow %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeLow)));
401 Log2(("EXT: u32TimeLastAccess %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastAccess),
402 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastAccess)), sz, sizeof(sz))));
403 Log2(("EXT: u32TimeLastChange %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastChange),
404 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastChange)), sz, sizeof(sz))));
405 Log2(("EXT: u32TimeLastModification %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastModification),
406 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastModification)), sz, sizeof(sz))));
407 Log2(("EXT: u32TimeDeletion %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeDeletion),
408 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeDeletion)), sz, sizeof(sz))));
409 Log2(("EXT: uGidLow %#RX16\n", RT_LE2H_U16(pInode->Core.uGidLow)));
410 Log2(("EXT: cHardLinks %#RU16\n", RT_LE2H_U16(pInode->Core.cHardLinks)));
411 Log2(("EXT: cBlocksLow %#RX32\n", RT_LE2H_U32(pInode->Core.cBlocksLow)));
412 Log2(("EXT: fFlags %#RX32\n", RT_LE2H_U32(pInode->Core.fFlags)));
413 Log2(("EXT: Osd1.u32LnxVersion %#RX32\n", RT_LE2H_U32(pInode->Core.Osd1.u32LnxVersion)));
414 for (unsigned i = 0; i < RT_ELEMENTS(pInode->Core.au32Block); i++)
415 Log2(("EXT: au32Block[%u] %#RX32\n", i, RT_LE2H_U32(pInode->Core.au32Block[i])));
416 Log2(("EXT: u32Version %#RX32\n", RT_LE2H_U32(pInode->Core.u32Version)));
417 Log2(("EXT: offExtAttrLow %#RX32\n", RT_LE2H_U32(pInode->Core.offExtAttrLow)));
418 Log2(("EXT: cbSizeHigh %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeHigh)));
419 Log2(("EXT: u32FragmentAddrObs %#RX32\n", RT_LE2H_U32(pInode->Core.u32FragmentAddrObs)));
420 Log2(("EXT: Osd2.Lnx.cBlocksHigh %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.cBlocksHigh)));
421 Log2(("EXT: Osd2.Lnx.offExtAttrHigh %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.offExtAttrHigh)));
422 Log2(("EXT: Osd2.Lnx.uUidHigh %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uUidHigh)));
423 Log2(("EXT: Osd2.Lnx.uGidHigh %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uGidHigh)));
424 Log2(("EXT: Osd2.Lnx.u16ChksumLow %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.u16ChksumLow)));
425
426 if (pThis->cbInode >= sizeof(EXTINODECOMB))
427 {
428 Log2(("EXT: cbInodeExtra %#RU16\n", RT_LE2H_U16(pInode->Extra.cbInodeExtra)));
429 Log2(("EXT: u16ChksumHigh %#RX16\n", RT_LE2H_U16(pInode->Extra.u16ChksumHigh)));
430 Log2(("EXT: u32ExtraTimeLastChange %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastChange)));
431 Log2(("EXT: u32ExtraTimeLastModification %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastModification)));
432 Log2(("EXT: u32ExtraTimeLastAccess %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastAccess)));
433 Log2(("EXT: u32TimeCreation %#RX32 %s\n", RT_LE2H_U32(pInode->Extra.u32TimeCreation),
434 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Extra.u32TimeCreation)), sz, sizeof(sz))));
435 Log2(("EXT: u32ExtraTimeCreation %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeCreation)));
436 Log2(("EXT: u32VersionHigh %#RX32\n", RT_LE2H_U16(pInode->Extra.u32VersionHigh)));
437 Log2(("EXT: u32ProjectId %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ProjectId)));
438 }
439 }
440}
441#endif
442
443
444/**
445 * Converts a block number to a byte offset.
446 *
447 * @returns Offset in bytes for the given block number.
448 * @param pThis The ext volume instance.
449 * @param iBlock The block number to convert.
450 */
451DECLINLINE(uint64_t) rtFsExtBlockIdxToDiskOffset(PRTFSEXTVOL pThis, uint64_t iBlock)
452{
453 return iBlock << pThis->cBlockShift;
454}
455
456
457/**
458 * Converts a byte offset to a block number.
459 *
460 * @returns Block number.
461 * @param pThis The ext volume instance.
462 * @param iBlock The offset to convert.
463 */
464DECLINLINE(uint64_t) rtFsExtDiskOffsetToBlockIdx(PRTFSEXTVOL pThis, uint64_t off)
465{
466 return off >> pThis->cBlockShift;
467}
468
469
470/**
471 * Creates the proper block number from the given low and high parts in case a 64bit
472 * filesystem is used.
473 *
474 * @returns 64bit block number.
475 * @param pThis The ext volume instance.
476 * @param uLow The lower 32bit part.
477 * @param uHigh The upper 32bit part.
478 */
479DECLINLINE(uint64_t) rtFsExtBlockFromLowHigh(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
480{
481 return pThis->f64Bit ? RT_MAKE_U64(uLow, uHigh): uLow;
482}
483
484
485/**
486 * Converts the given high and low parts of the block number to a byte offset.
487 *
488 * @returns Offset in bytes for the given block number.
489 * @param uLow The lower 32bit part of the block number.
490 * @param uHigh The upper 32bit part of the block number.
491 */
492DECLINLINE(uint64_t) rtFsExtBlockIdxLowHighToDiskOffset(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
493{
494 uint64_t iBlock = rtFsExtBlockFromLowHigh(pThis, uLow, uHigh);
495 return rtFsExtBlockIdxToDiskOffset(pThis, iBlock);
496}
497
498
499/**
500 * Allocates a new block group.
501 *
502 * @returns Pointer to the new block group descriptor or NULL if out of memory.
503 * @param pThis The ext volume instance.
504 * @param cbAlloc How much to allocate.
505 * @param iBlockGroup Block group number.
506 */
507static PRTFSEXTBLKGRP rtFsExtBlockGroupAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint32_t iBlockGroup)
508{
509 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTMemAllocZ(cbAlloc);
510 if (RT_LIKELY(pBlockGroup))
511 {
512 pBlockGroup->Core.Key = iBlockGroup;
513 pBlockGroup->cRefs = 0;
514 pBlockGroup->pabInodeBitmap = &pBlockGroup->abBlockBitmap[pThis->cbBlockBitmap];
515 pThis->cbBlockGroups += cbAlloc;
516 }
517
518 return pBlockGroup;
519}
520
521
522/**
523 * Frees the given block group.
524 *
525 * @returns nothing.
526 * @param pThis The ext volume instance.
527 * @param pBlockGroup The block group to free.
528 */
529static void rtFsExtBlockGroupFree(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
530{
531 Assert(!pBlockGroup->cRefs);
532
533 /*
534 * Put it into the cache if the limit wasn't exceeded, otherwise the block group
535 * is freed right away.
536 */
537 if (pThis->cbBlockGroups <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
538 {
539 /* Put onto the LRU list. */
540 RTListPrepend(&pThis->LstBlockGroupLru, &pBlockGroup->NdLru);
541 }
542 else
543 {
544 /* Remove from the tree and free memory. */
545 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
546 Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
547 RTMemFree(pBlockGroup);
548 pThis->cbBlockGroups -= sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
549 }
550}
551
552
553/**
554 * Returns a new block group utilizing the cache if possible.
555 *
556 * @returns Pointer to the new block group descriptor or NULL if out of memory.
557 * @param pThis The ext volume instance.
558 * @param iBlockGroup Block group number.
559 */
560static PRTFSEXTBLKGRP rtFsExtBlockGroupGetNew(PRTFSEXTVOL pThis, uint32_t iBlockGroup)
561{
562 PRTFSEXTBLKGRP pBlockGroup = NULL;
563 size_t cbAlloc = sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
564 if (pThis->cbBlockGroups + cbAlloc <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
565 pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
566 else
567 {
568 pBlockGroup = RTListRemoveLast(&pThis->LstBlockGroupLru, RTFSEXTBLKGRP, NdLru);
569 if (!pBlockGroup)
570 pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
571 else
572 {
573 /* Remove the block group from the tree because it gets a new key. */
574 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
575 Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
576 }
577 }
578
579 Assert(!pBlockGroup->cRefs);
580 pBlockGroup->Core.Key = iBlockGroup;
581 pBlockGroup->cRefs = 1;
582
583 return pBlockGroup;
584}
585
586
587/**
588 * Loads the given block group number and returns it on success.
589 *
590 * @returns IPRT status code.
591 * @param pThis The ext volume instance.
592 * @param iBlockGroup The block group to load.
593 * @param ppBlockGroup Where to store the block group on success.
594 */
595static int rtFsExtBlockGroupLoad(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PRTFSEXTBLKGRP *ppBlockGroup)
596{
597 int rc = VINF_SUCCESS;
598
599 /* Try to fetch the block group from the cache first. */
600 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTAvlU32Get(&pThis->BlockGroupRoot, iBlockGroup);
601 if (!pBlockGroup)
602 {
603 /* Slow path, load from disk. */
604 pBlockGroup = rtFsExtBlockGroupGetNew(pThis, iBlockGroup);
605 if (RT_LIKELY(pBlockGroup))
606 {
607 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pThis->cbBlock == _1K ? 2 : 1)
608 + (uint64_t)iBlockGroup * pThis->cbBlkGrpDesc;
609 EXTBLOCKGROUPDESC BlockGroupDesc;
610 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &BlockGroupDesc, pThis->cbBlkGrpDesc, NULL);
611 if (RT_SUCCESS(rc))
612 {
613#ifdef LOG_ENABLED
614 rtFsExtBlockGroup_Log(pThis, iBlockGroup, &BlockGroupDesc);
615#endif
616 pBlockGroup->iBlockInodeTbl = RT_LE2H_U32(BlockGroupDesc.v32.offInodeTableLow)
617 | ((pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
618 ? (uint64_t)RT_LE2H_U32(BlockGroupDesc.v64.offInodeTableHigh) << 32
619 : 0);
620
621 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offBlockBitmapLow),
622 RT_LE2H_U32(BlockGroupDesc.v64.offBlockBitmapHigh));
623 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->abBlockBitmap[0], pThis->cbBlockBitmap, NULL);
624 if (RT_SUCCESS(rc))
625 {
626 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offInodeBitmapLow),
627 RT_LE2H_U32(BlockGroupDesc.v64.offInodeBitmapHigh));
628 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->pabInodeBitmap[0], pThis->cbInodeBitmap, NULL);
629 if (RT_SUCCESS(rc))
630 {
631 bool fIns = RTAvlU32Insert(&pThis->BlockGroupRoot, &pBlockGroup->Core);
632 Assert(fIns); RT_NOREF(fIns);
633 }
634 }
635 }
636 }
637 else
638 rc = VERR_NO_MEMORY;
639 }
640 else
641 {
642 /* Remove from current LRU list position and add to the beginning. */
643 uint32_t cRefs = ASMAtomicIncU32(&pBlockGroup->cRefs);
644 if (cRefs == 1) /* Block groups get removed from the LRU list if they are referenced. */
645 RTListNodeRemove(&pBlockGroup->NdLru);
646 }
647
648 if (RT_SUCCESS(rc))
649 *ppBlockGroup = pBlockGroup;
650 else if (pBlockGroup)
651 {
652 ASMAtomicDecU32(&pBlockGroup->cRefs);
653 rtFsExtBlockGroupFree(pThis, pBlockGroup); /* Free the block group. */
654 }
655
656 return rc;
657}
658
659
660/**
661 * Releases a reference of the given block group.
662 *
663 * @returns nothing.
664 * @param pThis The ext volume instance.
665 * @param pBlockGroup The block group to release.
666 */
667static void rtFsExtBlockGroupRelease(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
668{
669 uint32_t cRefs = ASMAtomicDecU32(&pBlockGroup->cRefs);
670 if (!cRefs)
671 rtFsExtBlockGroupFree(pThis, pBlockGroup);
672}
673
674
675/**
676 * Allocates a new inode.
677 *
678 * @returns Pointer to the new inode or NULL if out of memory.
679 * @param pThis The ext volume instance.
680 * @param iInode Inode number.
681 */
682static PRTFSEXTINODE rtFsExtInodeAlloc(PRTFSEXTVOL pThis, uint32_t iInode)
683{
684 PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTMemAllocZ(sizeof(RTFSEXTINODE));
685 if (RT_LIKELY(pInode))
686 {
687 pInode->Core.Key = iInode;
688 pInode->cRefs = 0;
689 pThis->cbInodes += sizeof(RTFSEXTINODE);
690 }
691
692 return pInode;
693}
694
695
696/**
697 * Frees the given inode.
698 *
699 * @returns nothing.
700 * @param pThis The ext volume instance.
701 * @param pInode The inode to free.
702 */
703static void rtFsExtInodeFree(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
704{
705 Assert(!pInode->cRefs);
706
707 /*
708 * Put it into the cache if the limit wasn't exceeded, otherwise the inode
709 * is freed right away.
710 */
711 if (pThis->cbInodes <= RTFSEXT_MAX_INODE_CACHE_SIZE)
712 {
713 /* Put onto the LRU list. */
714 RTListPrepend(&pThis->LstInodeLru, &pInode->NdLru);
715 }
716 else
717 {
718 /* Remove from the tree and free memory. */
719 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
720 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
721 RTMemFree(pInode);
722 pThis->cbInodes -= sizeof(RTFSEXTINODE);
723 }
724}
725
726
727/**
728 * Returns a new inodep utilizing the cache if possible.
729 *
730 * @returns Pointer to the new inode or NULL if out of memory.
731 * @param pThis The ext volume instance.
732 * @param iInode Inode number.
733 */
734static PRTFSEXTINODE rtFsExtInodeGetNew(PRTFSEXTVOL pThis, uint32_t iInode)
735{
736 PRTFSEXTINODE pInode = NULL;
737 if (pThis->cbInodes + sizeof(RTFSEXTINODE) <= RTFSEXT_MAX_INODE_CACHE_SIZE)
738 pInode = rtFsExtInodeAlloc(pThis, iInode);
739 else
740 {
741 pInode = RTListRemoveLast(&pThis->LstInodeLru, RTFSEXTINODE, NdLru);
742 if (!pInode)
743 pInode = rtFsExtInodeAlloc(pThis, iInode);
744 else
745 {
746 /* Remove the block group from the tree because it gets a new key. */
747 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
748 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
749 }
750 }
751
752 Assert(!pInode->cRefs);
753 pInode->Core.Key = iInode;
754 pInode->cRefs = 1;
755
756 return pInode;
757}
758
759
760/**
761 * Loads the given inode number and returns it on success.
762 *
763 * @returns IPRT status code.
764 * @param pThis The ext volume instance.
765 * @param iInode The inode to load.
766 * @param ppInode Where to store the inode on success.
767 */
768static int rtFsExtInodeLoad(PRTFSEXTVOL pThis, uint32_t iInode, PRTFSEXTINODE *ppInode)
769{
770 int rc = VINF_SUCCESS;
771
772 /* Try to fetch the inode from the cache first. */
773 PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTAvlU32Get(&pThis->InodeRoot, iInode);
774 if (!pInode)
775 {
776 /* Slow path, load from disk. */
777 pInode = rtFsExtInodeGetNew(pThis, iInode);
778 if (RT_LIKELY(pInode))
779 {
780 /* Calculate the block group and load that one first to get at the inode table location. */
781 PRTFSEXTBLKGRP pBlockGroup = NULL;
782 rc = rtFsExtBlockGroupLoad(pThis, (iInode - 1) / pThis->cInodesPerGroup, &pBlockGroup);
783 if (RT_SUCCESS(rc))
784 {
785 uint32_t idxInodeInTbl = (iInode - 1) % pThis->cInodesPerGroup;
786 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pBlockGroup->iBlockInodeTbl)
787 + idxInodeInTbl * pThis->cbInode;
788
789 /* Release block group here already as it is not required. */
790 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
791
792 EXTINODECOMB Inode;
793 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &Inode, RT_MIN(sizeof(Inode), pThis->cbInode), NULL);
794 if (RT_SUCCESS(rc))
795 {
796#ifdef LOG_ENABLED
797 rtFsExtInode_Log(pThis, iInode, &Inode);
798#endif
799 /** @todo Fill in data. */
800 }
801 }
802 }
803 else
804 rc = VERR_NO_MEMORY;
805 }
806 else
807 {
808 /* Remove from current LRU list position and add to the beginning. */
809 uint32_t cRefs = ASMAtomicIncU32(&pInode->cRefs);
810 if (cRefs == 1) /* Inodes get removed from the LRU list if they are referenced. */
811 RTListNodeRemove(&pInode->NdLru);
812 }
813
814 if (RT_SUCCESS(rc))
815 *ppInode = pInode;
816 else if (pInode)
817 {
818 ASMAtomicDecU32(&pInode->cRefs);
819 rtFsExtInodeFree(pThis, pInode); /* Free the inode. */
820 }
821
822 return rc;
823}
824
825
826/**
827 * Releases a reference of the given inode.
828 *
829 * @returns nothing.
830 * @param pThis The ext volume instance.
831 * @param pInode The inode to release.
832 */
833static void rtFsExtInodeRelease(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
834{
835 uint32_t cRefs = ASMAtomicDecU32(&pInode->cRefs);
836 if (!cRefs)
837 rtFsExtInodeFree(pThis, pInode);
838}
839
840
841/*
842 *
843 * Directory instance methods
844 * Directory instance methods
845 * Directory instance methods
846 *
847 */
848
849/**
850 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
851 */
852static DECLCALLBACK(int) rtFsExtDir_Close(void *pvThis)
853{
854 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
855 LogFlowFunc(("pThis=%p\n", pThis));
856 RT_NOREF(pThis);
857 return VINF_SUCCESS;
858}
859
860
861/**
862 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
863 */
864static DECLCALLBACK(int) rtFsExtDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
865{
866 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
867 LogFlowFunc(("\n"));
868 RT_NOREF(pThis, pObjInfo, enmAddAttr);
869 return VERR_NOT_IMPLEMENTED;
870}
871
872
873/**
874 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
875 */
876static DECLCALLBACK(int) rtFsExtDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
877{
878 LogFlowFunc(("\n"));
879 RT_NOREF(pvThis, fMode, fMask);
880 return VERR_WRITE_PROTECT;
881}
882
883
884/**
885 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
886 */
887static DECLCALLBACK(int) rtFsExtDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
888 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
889{
890 LogFlowFunc(("\n"));
891 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
892 return VERR_WRITE_PROTECT;
893}
894
895
896/**
897 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
898 */
899static DECLCALLBACK(int) rtFsExtDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
900{
901 LogFlowFunc(("\n"));
902 RT_NOREF(pvThis, uid, gid);
903 return VERR_WRITE_PROTECT;
904}
905
906
907/**
908 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
909 */
910static DECLCALLBACK(int) rtFsExtDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
911 uint32_t fFlags, PRTVFSOBJ phVfsObj)
912{
913 LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
914 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
915 PRTFSEXTVOL pVol = pThis->pVol;
916 int rc = VINF_SUCCESS;
917
918 RT_NOREF(pThis, pVol, phVfsObj, pszEntry, fFlags);
919
920 /*
921 * We cannot create or replace anything, just open stuff.
922 */
923 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
924 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
925 { /* likely */ }
926 else
927 return VERR_WRITE_PROTECT;
928
929 LogFlow(("rtFsExtDir_Open(%s): returns %Rrc\n", pszEntry, rc));
930 return rc;
931}
932
933
934/**
935 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
936 */
937static DECLCALLBACK(int) rtFsExtDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
938{
939 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
940 LogFlowFunc(("\n"));
941 return VERR_WRITE_PROTECT;
942}
943
944
945/**
946 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
947 */
948static DECLCALLBACK(int) rtFsExtDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
949{
950 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
951 LogFlowFunc(("\n"));
952 return VERR_NOT_SUPPORTED;
953}
954
955
956/**
957 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
958 */
959static DECLCALLBACK(int) rtFsExtDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
960 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
961{
962 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
963 LogFlowFunc(("\n"));
964 return VERR_WRITE_PROTECT;
965}
966
967
968/**
969 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
970 */
971static DECLCALLBACK(int) rtFsExtDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
972{
973 RT_NOREF(pvThis, pszEntry, fType);
974 LogFlowFunc(("\n"));
975 return VERR_WRITE_PROTECT;
976}
977
978
979/**
980 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
981 */
982static DECLCALLBACK(int) rtFsExtDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
983{
984 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
985 LogFlowFunc(("\n"));
986 return VERR_WRITE_PROTECT;
987}
988
989
990/**
991 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
992 */
993static DECLCALLBACK(int) rtFsExtDir_RewindDir(void *pvThis)
994{
995 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
996 LogFlowFunc(("\n"));
997
998 pThis->fNoMoreFiles = false;
999 return VINF_SUCCESS;
1000}
1001
1002
1003/**
1004 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
1005 */
1006static DECLCALLBACK(int) rtFsExtDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
1007 RTFSOBJATTRADD enmAddAttr)
1008{
1009 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
1010 int rc = VINF_SUCCESS;
1011 LogFlowFunc(("\n"));
1012
1013 RT_NOREF(pThis, rc, pDirEntry, pcbDirEntry, enmAddAttr);
1014
1015 /*
1016 * The End.
1017 */
1018 LogFlowFunc(("no more files\n"));
1019 pThis->fNoMoreFiles = true;
1020 return VERR_NO_MORE_FILES;
1021}
1022
1023
1024/**
1025 * EXT directory operations.
1026 */
1027static const RTVFSDIROPS g_rtFsExtDirOps =
1028{
1029 { /* Obj */
1030 RTVFSOBJOPS_VERSION,
1031 RTVFSOBJTYPE_DIR,
1032 "EXT Dir",
1033 rtFsExtDir_Close,
1034 rtFsExtDir_QueryInfo,
1035 RTVFSOBJOPS_VERSION
1036 },
1037 RTVFSDIROPS_VERSION,
1038 0,
1039 { /* ObjSet */
1040 RTVFSOBJSETOPS_VERSION,
1041 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
1042 rtFsExtDir_SetMode,
1043 rtFsExtDir_SetTimes,
1044 rtFsExtDir_SetOwner,
1045 RTVFSOBJSETOPS_VERSION
1046 },
1047 rtFsExtDir_Open,
1048 NULL /* pfnFollowAbsoluteSymlink */,
1049 NULL /* pfnOpenFile */,
1050 NULL /* pfnOpenDir */,
1051 rtFsExtDir_CreateDir,
1052 rtFsExtDir_OpenSymlink,
1053 rtFsExtDir_CreateSymlink,
1054 NULL /* pfnQueryEntryInfo */,
1055 rtFsExtDir_UnlinkEntry,
1056 rtFsExtDir_RenameEntry,
1057 rtFsExtDir_RewindDir,
1058 rtFsExtDir_ReadDir,
1059 RTVFSDIROPS_VERSION,
1060};
1061
1062
1063/**
1064 * Opens a directory by the given inode.
1065 *
1066 * @returns IPRT status code.
1067 * @param pThis The ext volume instance.
1068 * @param iInode The inode to open.
1069 * @param phVfsDir Where to store the handle to the VFS directory on success.
1070 */
1071static int rtFsExtVol_OpenDirByInode(PRTFSEXTVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir)
1072{
1073 PRTFSEXTINODE pInode = NULL;
1074 int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
1075 if (RT_SUCCESS(rc))
1076 rtFsExtInodeRelease(pThis, pInode);
1077 RT_NOREF(phVfsDir, g_rtFsExtDirOps);
1078 return rc;
1079}
1080
1081
1082/*
1083 *
1084 * Volume level code.
1085 * Volume level code.
1086 * Volume level code.
1087 *
1088 */
1089
1090
1091/**
1092 * Checks whether the block range in the given block group is in use by checking the
1093 * block bitmap.
1094 *
1095 * @returns Flag whether the range is in use.
1096 * @param pBlkGrpDesc The block group to check for.
1097 * @param iBlockStart The starting block to check relative from the beginning of the block group.
1098 * @param cBlocks How many blocks to check.
1099 */
1100static bool rtFsExtIsBlockRangeInUse(PRTFSEXTBLKGRP pBlkGrpDesc, uint64_t iBlockStart, size_t cBlocks)
1101{
1102 /** @todo: Optimize with ASMBitFirstSet(). */
1103 while (cBlocks)
1104 {
1105 uint32_t idxByte = iBlockStart / 8;
1106 uint32_t iBit = iBlockStart % 8;
1107
1108 if (pBlkGrpDesc->abBlockBitmap[idxByte] & RT_BIT(iBit))
1109 return true;
1110
1111 cBlocks--;
1112 iBlockStart++;
1113 }
1114
1115 return false;
1116}
1117
1118
1119static DECLCALLBACK(int) rtFsExtVolBlockGroupTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
1120{
1121 RT_NOREF(pvUser);
1122
1123 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)pCore;
1124 Assert(!pBlockGroup->cRefs);
1125 RTMemFree(pBlockGroup);
1126 return VINF_SUCCESS;
1127}
1128
1129
1130/**
1131 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1132 */
1133static DECLCALLBACK(int) rtFsExtVol_Close(void *pvThis)
1134{
1135 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
1136
1137 /* Destroy the block group tree. */
1138 RTAvlU32Destroy(&pThis->BlockGroupRoot, rtFsExtVolBlockGroupTreeDestroy, pThis);
1139 pThis->BlockGroupRoot = NULL;
1140 RTListInit(&pThis->LstBlockGroupLru);
1141
1142 /*
1143 * Backing file and handles.
1144 */
1145 RTVfsFileRelease(pThis->hVfsBacking);
1146 pThis->hVfsBacking = NIL_RTVFSFILE;
1147 pThis->hVfsSelf = NIL_RTVFS;
1148
1149 return VINF_SUCCESS;
1150}
1151
1152
1153/**
1154 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1155 */
1156static DECLCALLBACK(int) rtFsExtVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1157{
1158 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
1159 return VERR_WRONG_TYPE;
1160}
1161
1162
1163/**
1164 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
1165 */
1166static DECLCALLBACK(int) rtFsExtVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
1167{
1168 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
1169 int rc = rtFsExtVol_OpenDirByInode(pThis, EXT_INODE_NR_ROOT_DIR, phVfsDir);
1170 LogFlowFunc(("returns %Rrc\n", rc));
1171 return rc;
1172}
1173
1174
1175/**
1176 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
1177 */
1178static DECLCALLBACK(int) rtFsExtVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
1179{
1180 int rc = VINF_SUCCESS;
1181 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
1182
1183 *pfUsed = false;
1184
1185 uint64_t iBlock = rtFsExtDiskOffsetToBlockIdx(pThis, off);
1186 uint64_t cBlocks = rtFsExtDiskOffsetToBlockIdx(pThis, cb) + (cb % pThis->cbBlock ? 1 : 0);
1187 while (cBlocks > 0)
1188 {
1189 uint32_t const iBlockGroup = iBlock / pThis->cBlocksPerGroup;
1190 uint32_t const iBlockRelStart = iBlock - iBlockGroup * pThis->cBlocksPerGroup;
1191 PRTFSEXTBLKGRP pBlockGroup = NULL;
1192
1193 rc = rtFsExtBlockGroupLoad(pThis, iBlockGroup, &pBlockGroup);
1194 if (RT_FAILURE(rc))
1195 break;
1196
1197 uint64_t cBlocksThis = RT_MIN(cBlocks, iBlockRelStart - pThis->cBlocksPerGroup);
1198 if (rtFsExtIsBlockRangeInUse(pBlockGroup, iBlockRelStart, cBlocksThis))
1199 {
1200 *pfUsed = true;
1201 break;
1202 }
1203
1204 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
1205 cBlocks -= cBlocksThis;
1206 iBlock += cBlocksThis;
1207 }
1208
1209 return rc;
1210}
1211
1212
1213DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsExtVolOps =
1214{
1215 /* .Obj = */
1216 {
1217 /* .uVersion = */ RTVFSOBJOPS_VERSION,
1218 /* .enmType = */ RTVFSOBJTYPE_VFS,
1219 /* .pszName = */ "ExtVol",
1220 /* .pfnClose = */ rtFsExtVol_Close,
1221 /* .pfnQueryInfo = */ rtFsExtVol_QueryInfo,
1222 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
1223 },
1224 /* .uVersion = */ RTVFSOPS_VERSION,
1225 /* .fFeatures = */ 0,
1226 /* .pfnOpenRoot = */ rtFsExtVol_OpenRoot,
1227 /* .pfnQueryRangeState = */ rtFsExtVol_QueryRangeState,
1228 /* .uEndMarker = */ RTVFSOPS_VERSION
1229};
1230
1231
1232
1233/**
1234 * Loads the parameters from the given original ext2 format superblock (EXT_SB_REV_ORIG).
1235 *
1236 * @returns IPRT status code.
1237 * @param pThis The ext volume instance.
1238 * @param pSb The superblock to load.
1239 * @param pErrInfo Where to return additional error info.
1240 */
1241static int rtFsExtVolLoadAndParseSuperBlockV0(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
1242{
1243 RT_NOREF(pErrInfo);
1244
1245 /*
1246 * Linux never supported a differing cluster (also called fragment) size for
1247 * the original ext2 layout so we reject such filesystems as it is not clear what
1248 * the purpose is really.
1249 */
1250 if (RT_LE2H_U32(pSb->cLogBlockSize) != RT_LE2H_U32(pSb->cLogClusterSize))
1251 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem cluster and block size differ");
1252
1253 pThis->f64Bit = false;
1254 pThis->cBlockShift = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
1255 pThis->cbBlock = UINT64_C(1) << pThis->cBlockShift;
1256 pThis->cbInode = sizeof(EXTINODE);
1257 pThis->cbBlkGrpDesc = sizeof(EXTBLOCKGROUPDESC32);
1258 pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
1259 pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
1260 pThis->cBlockGroups = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
1261 pThis->cbBlockBitmap = pThis->cBlocksPerGroup / 8;
1262 if (pThis->cBlocksPerGroup % 8)
1263 pThis->cbBlockBitmap++;
1264 pThis->cbInodeBitmap = pThis->cInodesPerGroup / 8;
1265 if (pThis->cInodesPerGroup % 8)
1266 pThis->cbInodeBitmap++;
1267
1268 return VINF_SUCCESS;
1269}
1270
1271
1272/**
1273 * Loads the parameters from the given ext superblock (EXT_SB_REV_V2_DYN_INODE_SZ).
1274 *
1275 * @returns IPRT status code.
1276 * @param pThis The ext volume instance.
1277 * @param pSb The superblock to load.
1278 * @param pErrInfo Where to return additional error info.
1279 */
1280static int rtFsExtVolLoadAndParseSuperBlockV1(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
1281{
1282 if (RT_LE2H_U32(pSb->fFeaturesIncompat) != 0)
1283 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported incompatible features: %RX32",
1284 RT_LE2H_U32(pSb->fFeaturesIncompat));
1285 if ( RT_LE2H_U32(pSb->fFeaturesCompatRo) != 0
1286 && !(pThis->fMntFlags & RTVFSMNT_F_READ_ONLY))
1287 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported readonly features: %RX32",
1288 RT_LE2H_U32(pSb->fFeaturesCompatRo));
1289
1290 return VINF_SUCCESS;
1291}
1292
1293
1294/**
1295 * Loads and parses the superblock of the filesystem.
1296 *
1297 * @returns IPRT status code.
1298 * @param pThis The ext volume instance.
1299 * @param pErrInfo Where to return additional error info.
1300 */
1301static int rtFsExtVolLoadAndParseSuperblock(PRTFSEXTVOL pThis, PRTERRINFO pErrInfo)
1302{
1303 int rc = VINF_SUCCESS;
1304 EXTSUPERBLOCK Sb;
1305 rc = RTVfsFileReadAt(pThis->hVfsBacking, EXT_SB_OFFSET, &Sb, sizeof(EXTSUPERBLOCK), NULL);
1306 if (RT_FAILURE(rc))
1307 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
1308
1309 /* Validate the superblock. */
1310 if (RT_LE2H_U16(Sb.u16Signature) != EXT_SB_SIGNATURE)
1311 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not EXT - Signature mismatch: %RX16", RT_LE2H_U16(Sb.u16Signature));
1312
1313#ifdef LOG_ENABLED
1314 rtFsExtSb_Log(&Sb);
1315#endif
1316
1317 if (RT_LE2H_U16(Sb.u16FilesystemState) == EXT_SB_STATE_ERRORS)
1318 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains errors");
1319
1320 if (RT_LE2H_U32(Sb.u32RevLvl) == EXT_SB_REV_ORIG)
1321 rc = rtFsExtVolLoadAndParseSuperBlockV0(pThis, &Sb, pErrInfo);
1322 else
1323 rc = rtFsExtVolLoadAndParseSuperBlockV1(pThis, &Sb, pErrInfo);
1324
1325 return rc;
1326}
1327
1328
1329RTDECL(int) RTFsExtVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fExtFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
1330{
1331 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
1332 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
1333 AssertReturn(!fExtFlags, VERR_INVALID_FLAGS);
1334
1335 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
1336 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1337
1338 /*
1339 * Create a VFS instance and initialize the data so rtFsExtVol_Close works.
1340 */
1341 RTVFS hVfs;
1342 PRTFSEXTVOL pThis;
1343 int rc = RTVfsNew(&g_rtFsExtVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
1344 if (RT_SUCCESS(rc))
1345 {
1346 pThis->hVfsBacking = hVfsFileIn;
1347 pThis->hVfsSelf = hVfs;
1348 pThis->fMntFlags = fMntFlags;
1349 pThis->fExtFlags = fExtFlags;
1350 pThis->BlockGroupRoot = NULL;
1351 pThis->InodeRoot = NULL;
1352 pThis->cbBlockGroups = 0;
1353 pThis->cbInodes = 0;
1354 RTListInit(&pThis->LstBlockGroupLru);
1355 RTListInit(&pThis->LstInodeLru);
1356
1357 rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
1358 if (RT_SUCCESS(rc))
1359 {
1360 rc = rtFsExtVolLoadAndParseSuperblock(pThis, pErrInfo);
1361 if (RT_SUCCESS(rc))
1362 {
1363 *phVfs = hVfs;
1364 return VINF_SUCCESS;
1365 }
1366 }
1367
1368 RTVfsRelease(hVfs);
1369 *phVfs = NIL_RTVFS;
1370 }
1371 else
1372 RTVfsFileRelease(hVfsFileIn);
1373
1374 return rc;
1375}
1376
1377
1378/**
1379 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
1380 */
1381static DECLCALLBACK(int) rtVfsChainExtVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
1382 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
1383{
1384 RT_NOREF(pProviderReg);
1385
1386 /*
1387 * Basic checks.
1388 */
1389 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
1390 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
1391 if ( pElement->enmType != RTVFSOBJTYPE_VFS
1392 && pElement->enmType != RTVFSOBJTYPE_DIR)
1393 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
1394 if (pElement->cArgs > 1)
1395 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
1396
1397 /*
1398 * Parse the flag if present, save in pElement->uProvider.
1399 */
1400 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
1401 if (pElement->cArgs > 0)
1402 {
1403 const char *psz = pElement->paArgs[0].psz;
1404 if (*psz)
1405 {
1406 if (!strcmp(psz, "ro"))
1407 fReadOnly = true;
1408 else if (!strcmp(psz, "rw"))
1409 fReadOnly = false;
1410 else
1411 {
1412 *poffError = pElement->paArgs[0].offSpec;
1413 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
1414 }
1415 }
1416 }
1417
1418 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
1419 return VINF_SUCCESS;
1420}
1421
1422
1423/**
1424 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
1425 */
1426static DECLCALLBACK(int) rtVfsChainExtVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
1427 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
1428 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1429{
1430 RT_NOREF(pProviderReg, pSpec, poffError);
1431
1432 int rc;
1433 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
1434 if (hVfsFileIn != NIL_RTVFSFILE)
1435 {
1436 RTVFS hVfs;
1437 rc = RTFsExtVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
1438 RTVfsFileRelease(hVfsFileIn);
1439 if (RT_SUCCESS(rc))
1440 {
1441 *phVfsObj = RTVfsObjFromVfs(hVfs);
1442 RTVfsRelease(hVfs);
1443 if (*phVfsObj != NIL_RTVFSOBJ)
1444 return VINF_SUCCESS;
1445 rc = VERR_VFS_CHAIN_CAST_FAILED;
1446 }
1447 }
1448 else
1449 rc = VERR_VFS_CHAIN_CAST_FAILED;
1450 return rc;
1451}
1452
1453
1454/**
1455 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
1456 */
1457static DECLCALLBACK(bool) rtVfsChainExtVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
1458 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
1459 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
1460{
1461 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
1462 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
1463 || !pReuseElement->paArgs[0].uProvider)
1464 return true;
1465 return false;
1466}
1467
1468
1469/** VFS chain element 'ext'. */
1470static RTVFSCHAINELEMENTREG g_rtVfsChainExtVolReg =
1471{
1472 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1473 /* fReserved = */ 0,
1474 /* pszName = */ "ext",
1475 /* ListEntry = */ { NULL, NULL },
1476 /* pszHelp = */ "Open a EXT file system, requires a file object on the left side.\n"
1477 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
1478 /* pfnValidate = */ rtVfsChainExtVol_Validate,
1479 /* pfnInstantiate = */ rtVfsChainExtVol_Instantiate,
1480 /* pfnCanReuseElement = */ rtVfsChainExtVol_CanReuseElement,
1481 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1482};
1483
1484RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainExtVolReg, rtVfsChainExtVolReg);
1485
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette