VirtualBox

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

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

scm

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

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