VirtualBox

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

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

Runtime/formats/ext: Add more structures

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/* $Id: extvfs.cpp 76256 2018-12-16 20:36:28Z 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/assert.h>
35#include <iprt/file.h>
36#include <iprt/log.h>
37#include <iprt/mem.h>
38#include <iprt/string.h>
39#include <iprt/vfs.h>
40#include <iprt/vfslowlevel.h>
41#include <iprt/formats/ext.h>
42
43
44/*********************************************************************************************************************************
45* Structures and Typedefs *
46*********************************************************************************************************************************/
47/**
48 * Cached block group descriptor data.
49 */
50typedef struct RTFSEXTBLKGRP
51{
52 /** Start offset (in bytes and from the start of the disk). */
53 uint64_t offStart;
54 /** Last offset in the block group (inclusive). */
55 uint64_t offLast;
56 /** Block bitmap - variable in size (depends on the block size
57 * and number of blocks per group). */
58 uint8_t abBlockBitmap[1];
59} RTFSEXTBLKGRP;
60/** Pointer to block group descriptor data. */
61typedef RTFSEXTBLKGRP *PRTFSEXTBLKGRP;
62
63/**
64 * Ext2/3/4 filesystem volume.
65 */
66typedef struct RTFSEXTVOL
67{
68 /** Handle to itself. */
69 RTVFS hVfsSelf;
70 /** The file, partition, or whatever backing the ext volume. */
71 RTVFSFILE hVfsBacking;
72 /** The size of the backing thingy. */
73 uint64_t cbBacking;
74 /** The formatted size of the volume. */
75 uint64_t cbVolume;
76 /** cbVolume expressed as a cluster count. */
77 uint64_t cClusters;
78
79 /** RTVFSMNT_F_XXX. */
80 uint32_t fMntFlags;
81 /** RTFSEXTVFS_F_XXX (currently none defined). */
82 uint32_t fExtFlags;
83
84 /** The (logical) sector size. */
85 uint32_t cbSector;
86
87 /** The (logical) cluster size. */
88 uint32_t cbCluster;
89
90 /** Block number of the superblock. */
91 uint32_t iSbBlock;
92 /** Size of one block. */
93 size_t cbBlock;
94 /** Number of blocks in one group. */
95 unsigned cBlocksPerGroup;
96 /** Number of blocks groups in the volume. */
97 unsigned cBlockGroups;
98 /** Cached block group descriptor data. */
99 PRTFSEXTBLKGRP pBlkGrpDesc;
100} RTFSEXTVOL;
101/** Pointer to the ext filesystem data. */
102typedef RTFSEXTVOL *PRTFSEXTVOL;
103
104
105
106/**
107 * Loads the block descriptor of the given block group from the medium.
108 *
109 * @returns IPRT status code.
110 * @param pThis EXT filesystem instance data.
111 * @param iBlkGrp Block group number to load.
112 */
113static int rtFsExtLoadBlkGrpDesc(PRTFSEXTVOL pThis, uint32_t iBlkGrp)
114{
115 size_t cbBlockBitmap = pThis->cBlocksPerGroup / 8;
116 if (pThis->cBlocksPerGroup % 8)
117 cbBlockBitmap++;
118
119 PRTFSEXTBLKGRP pBlkGrpDesc = pThis->pBlkGrpDesc;
120 if (!pBlkGrpDesc)
121 {
122 size_t cbBlkDesc = RT_UOFFSETOF_DYN(RTFSEXTBLKGRP, abBlockBitmap[cbBlockBitmap]);
123 pBlkGrpDesc = (PRTFSEXTBLKGRP)RTMemAllocZ(cbBlkDesc);
124 if (!pBlkGrpDesc)
125 return VERR_NO_MEMORY;
126 }
127
128 uint64_t offRead = (pThis->iSbBlock + 1) * pThis->cbBlock;
129 EXTBLOCKGROUPDESC32 BlkDesc;
130 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &BlkDesc, sizeof(BlkDesc), NULL);
131 if (RT_SUCCESS(rc))
132 {
133 pBlkGrpDesc->offStart = pThis->iSbBlock + (uint64_t)iBlkGrp * pThis->cBlocksPerGroup * pThis->cbBlock;
134 pBlkGrpDesc->offLast = pBlkGrpDesc->offStart + pThis->cBlocksPerGroup * pThis->cbBlock;
135 rc = RTVfsFileReadAt(pThis->hVfsBacking, BlkDesc.offBlockBitmapLow * pThis->cbBlock,
136 &pBlkGrpDesc->abBlockBitmap[0], cbBlockBitmap, NULL);
137 }
138
139 pThis->pBlkGrpDesc = pBlkGrpDesc;
140 return rc;
141}
142
143
144static bool rtFsExtIsBlockRangeInUse(PRTFSEXTBLKGRP pBlkGrpDesc, uint32_t offBlockStart, size_t cBlocks)
145{
146 while (cBlocks)
147 {
148 uint32_t idxByte = offBlockStart / 8;
149 uint32_t iBit = offBlockStart % 8;
150
151 if (pBlkGrpDesc->abBlockBitmap[idxByte] & RT_BIT(iBit))
152 return true;
153
154 cBlocks--;
155 offBlockStart++;
156 }
157
158 return false;
159}
160
161
162/**
163 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
164 */
165static DECLCALLBACK(int) rtFsExtVol_Close(void *pvThis)
166{
167 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
168
169 if (pThis->pBlkGrpDesc)
170 RTMemFree(pThis->pBlkGrpDesc);
171
172 return VINF_SUCCESS;
173}
174
175
176/**
177 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
178 */
179static DECLCALLBACK(int) rtFsExtVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
180{
181 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
182 return VERR_WRONG_TYPE;
183}
184
185
186/**
187 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
188 */
189static DECLCALLBACK(int) rtFsExtVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
190{
191 NOREF(pvThis);
192 NOREF(phVfsDir);
193 return VERR_NOT_IMPLEMENTED;
194}
195
196
197/**
198 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
199 */
200static DECLCALLBACK(int) rtFsExtVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
201{
202 int rc = VINF_SUCCESS;
203 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
204
205 *pfUsed = false;
206
207 while (cb > 0)
208 {
209 uint32_t const offBlockStart = (uint32_t)(off / pThis->cbBlock);
210 uint32_t const iBlockGroup = (offBlockStart - pThis->iSbBlock) / pThis->cBlocksPerGroup;
211 uint32_t const offBlockRelStart = offBlockStart - iBlockGroup * pThis->cBlocksPerGroup;
212
213 if ( off < pThis->pBlkGrpDesc->offStart
214 || off > pThis->pBlkGrpDesc->offLast)
215 {
216 /* Load new block descriptor. */
217 rc = rtFsExtLoadBlkGrpDesc(pThis, iBlockGroup);
218 if (RT_FAILURE(rc))
219 break;
220 }
221
222 size_t cbThis = RT_MIN(cb, pThis->pBlkGrpDesc->offLast - off + 1);
223 if (rtFsExtIsBlockRangeInUse(pThis->pBlkGrpDesc,
224 offBlockRelStart,
225 cbThis / pThis->cbBlock + (cbThis % pThis->cbBlock ? 1 : 0)) )
226 {
227 *pfUsed = true;
228 break;
229 }
230
231 cb -= cbThis;
232 off += cbThis;
233 }
234
235 return rc;
236}
237
238
239DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsExtVolOps =
240{
241 /* .Obj = */
242 {
243 /* .uVersion = */ RTVFSOBJOPS_VERSION,
244 /* .enmType = */ RTVFSOBJTYPE_VFS,
245 /* .pszName = */ "ExtVol",
246 /* .pfnClose = */ rtFsExtVol_Close,
247 /* .pfnQueryInfo = */ rtFsExtVol_QueryInfo,
248 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
249 },
250 /* .uVersion = */ RTVFSOPS_VERSION,
251 /* .fFeatures = */ 0,
252 /* .pfnOpenRoot = */ rtFsExtVol_OpenRoot,
253 /* .pfnQueryRangeState = */ rtFsExtVol_QueryRangeState,
254 /* .uEndMarker = */ RTVFSOPS_VERSION
255};
256
257
258RTDECL(int) RTFsExtVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fExtFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
259{
260 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
261 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
262 AssertReturn(!fExtFlags, VERR_INVALID_FLAGS);
263
264 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
265 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
266
267 PRTFSEXTVOL pThis;
268 int rc = RTVfsNew(&g_rtFsExtVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, phVfs, (void **)&pThis);
269 if (RT_SUCCESS(rc))
270 {
271 pThis->hVfsBacking = hVfsFileIn;
272 pThis->pBlkGrpDesc = NULL;
273
274 EXTSUPERBLOCK SuperBlock;
275 rc = RTVfsFileReadAt(hVfsFileIn, 1024, &SuperBlock, sizeof(EXTSUPERBLOCK), NULL);
276 if (RT_SUCCESS(rc))
277 {
278#if defined(RT_BIGENDIAN)
279 /** @todo Convert to host endianess. */
280#endif
281 if (SuperBlock.u16FilesystemState == EXT_SB_STATE_ERRORS)
282 rc = RTERRINFO_LOG_SET(pErrInfo, VERR_FILESYSTEM_CORRUPT, "EXT_STATE_ERRORS");
283 else
284 {
285 pThis->iSbBlock = SuperBlock.iBlockOfSuperblock;
286 pThis->cbBlock = 1024 << SuperBlock.cLogBlockSize;
287 pThis->cBlocksPerGroup = SuperBlock.cBlocksPerGroup;
288 pThis->cBlockGroups = SuperBlock.cBlocksTotalLow / pThis->cBlocksPerGroup;
289
290 /* Load first block group descriptor. */
291 rc = rtFsExtLoadBlkGrpDesc(pThis, 0);
292 }
293 if (RT_SUCCESS(rc))
294 {
295 return VINF_SUCCESS;
296 }
297 }
298 else
299 rc = RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
300
301 RTVfsRelease(*phVfs);
302 *phVfs = NIL_RTVFS;
303 }
304 else
305 RTVfsFileRelease(hVfsFileIn);
306
307 return rc;
308}
309
310
311/**
312 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
313 */
314static DECLCALLBACK(int) rtVfsChainExtVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
315 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
316{
317 RT_NOREF(pProviderReg);
318
319 /*
320 * Basic checks.
321 */
322 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
323 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
324 if ( pElement->enmType != RTVFSOBJTYPE_VFS
325 && pElement->enmType != RTVFSOBJTYPE_DIR)
326 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
327 if (pElement->cArgs > 1)
328 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
329
330 /*
331 * Parse the flag if present, save in pElement->uProvider.
332 */
333 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
334 if (pElement->cArgs > 0)
335 {
336 const char *psz = pElement->paArgs[0].psz;
337 if (*psz)
338 {
339 if (!strcmp(psz, "ro"))
340 fReadOnly = true;
341 else if (!strcmp(psz, "rw"))
342 fReadOnly = false;
343 else
344 {
345 *poffError = pElement->paArgs[0].offSpec;
346 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
347 }
348 }
349 }
350
351 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
352 return VINF_SUCCESS;
353}
354
355
356/**
357 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
358 */
359static DECLCALLBACK(int) rtVfsChainExtVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
360 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
361 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
362{
363 RT_NOREF(pProviderReg, pSpec, poffError);
364
365 int rc;
366 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
367 if (hVfsFileIn != NIL_RTVFSFILE)
368 {
369 RTVFS hVfs;
370 rc = RTFsExtVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
371 RTVfsFileRelease(hVfsFileIn);
372 if (RT_SUCCESS(rc))
373 {
374 *phVfsObj = RTVfsObjFromVfs(hVfs);
375 RTVfsRelease(hVfs);
376 if (*phVfsObj != NIL_RTVFSOBJ)
377 return VINF_SUCCESS;
378 rc = VERR_VFS_CHAIN_CAST_FAILED;
379 }
380 }
381 else
382 rc = VERR_VFS_CHAIN_CAST_FAILED;
383 return rc;
384}
385
386
387/**
388 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
389 */
390static DECLCALLBACK(bool) rtVfsChainExtVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
391 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
392 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
393{
394 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
395 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
396 || !pReuseElement->paArgs[0].uProvider)
397 return true;
398 return false;
399}
400
401
402/** VFS chain element 'ext'. */
403static RTVFSCHAINELEMENTREG g_rtVfsChainExtVolReg =
404{
405 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
406 /* fReserved = */ 0,
407 /* pszName = */ "ext",
408 /* ListEntry = */ { NULL, NULL },
409 /* pszHelp = */ "Open a EXT file system, requires a file object on the left side.\n"
410 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
411 /* pfnValidate = */ rtVfsChainExtVol_Validate,
412 /* pfnInstantiate = */ rtVfsChainExtVol_Instantiate,
413 /* pfnCanReuseElement = */ rtVfsChainExtVol_CanReuseElement,
414 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
415};
416
417RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainExtVolReg, rtVfsChainExtVolReg);
418
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