VirtualBox

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

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

Runtime/fs/extvfs: Updates

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