1 | /* $Id: fatvfs.cpp 66615 2017-04-19 19:29:36Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * IPRT - FAT Virtual Filesystem.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2017 Oracle Corporation
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | * available from http://www.215389.xyz. 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 | #include "internal/iprt.h"
|
---|
32 | #include <iprt/fs.h>
|
---|
33 |
|
---|
34 | #include <iprt/asm.h>
|
---|
35 | #include <iprt/assert.h>
|
---|
36 | #include <iprt/ctype.h>
|
---|
37 | #include <iprt/file.h>
|
---|
38 | #include <iprt/err.h>
|
---|
39 | #include <iprt/mem.h>
|
---|
40 | #include <iprt/poll.h>
|
---|
41 | #include <iprt/string.h>
|
---|
42 | #include <iprt/sg.h>
|
---|
43 | #include <iprt/thread.h>
|
---|
44 | #include <iprt/vfs.h>
|
---|
45 | #include <iprt/vfslowlevel.h>
|
---|
46 | #include <iprt/formats/fat.h>
|
---|
47 |
|
---|
48 |
|
---|
49 | /*********************************************************************************************************************************
|
---|
50 | * Defined Constants And Macros *
|
---|
51 | *********************************************************************************************************************************/
|
---|
52 |
|
---|
53 |
|
---|
54 | /*********************************************************************************************************************************
|
---|
55 | * Structures and Typedefs *
|
---|
56 | *********************************************************************************************************************************/
|
---|
57 | /**
|
---|
58 | * A part of the cluster chain covering up to 252 clusters.
|
---|
59 | */
|
---|
60 | typedef struct RTFSFATCHAINPART
|
---|
61 | {
|
---|
62 | /** List entry. */
|
---|
63 | RTLISTNODE ListEntry;
|
---|
64 | /** Chain entries. */
|
---|
65 | uint32_t aEntries[256 - 4];
|
---|
66 | } RTFSFATCHAINPART;
|
---|
67 | AssertCompile(sizeof(RTFSFATCHAINPART) <= _1K);
|
---|
68 | typedef RTFSFATCHAINPART *PRTFSFATCHAINPART;
|
---|
69 | typedef RTFSFATCHAINPART const *PCRTFSFATCHAINPART;
|
---|
70 |
|
---|
71 | /**
|
---|
72 | * A FAT cluster chain.
|
---|
73 | */
|
---|
74 | typedef struct RTFSFATCHAIN
|
---|
75 | {
|
---|
76 | /** The chain size in bytes. */
|
---|
77 | uint32_t cbChain;
|
---|
78 | /** The chain size in entries. */
|
---|
79 | uint32_t cClusters;
|
---|
80 | /** List of chain parts (RTFSFATCHAINPART). */
|
---|
81 | RTLISTANCHOR ListParts;
|
---|
82 | } RTFSFATCHAIN;
|
---|
83 | typedef RTFSFATCHAIN *PRTFSFATCHAIN;
|
---|
84 | typedef RTFSFATCHAIN const *PCRTFSFATCHAIN;
|
---|
85 |
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * FAT file system object (common part to files and dirs).
|
---|
89 | */
|
---|
90 | typedef struct RTFSFATOBJ
|
---|
91 | {
|
---|
92 | /** The parent directory keeps a list of open objects (RTFSFATOBJ). */
|
---|
93 | RTLISTNODE Entry;
|
---|
94 | /** The byte offset of the directory entry.
|
---|
95 | * This is set to UINT64_MAX if special FAT12/16 root directory. */
|
---|
96 | uint64_t offDirEntry;
|
---|
97 | /** Attributes. */
|
---|
98 | RTFMODE fAttrib;
|
---|
99 | /** The object size. */
|
---|
100 | uint32_t cbObject;
|
---|
101 | /** The access time. */
|
---|
102 | RTTIMESPEC AccessTime;
|
---|
103 | /** The modificaton time. */
|
---|
104 | RTTIMESPEC ModificationTime;
|
---|
105 | /** The birth time. */
|
---|
106 | RTTIMESPEC BirthTime;
|
---|
107 | /** Cluster chain. */
|
---|
108 | RTFSFATCHAIN Clusters;
|
---|
109 | /** Pointer to the volume. */
|
---|
110 | struct RTFSFATVOL *pVol;
|
---|
111 | } RTFSFATOBJ;
|
---|
112 | typedef RTFSFATOBJ *PRTFSFATOBJ;
|
---|
113 |
|
---|
114 | typedef struct RTFSFATFILE
|
---|
115 | {
|
---|
116 | /** Core FAT object info. */
|
---|
117 | RTFSFATOBJ Core;
|
---|
118 | /** The current file offset. */
|
---|
119 | uint32_t offFile;
|
---|
120 | } RTFSFATFILE;
|
---|
121 | typedef RTFSFATFILE *PRTFSFATFILE;
|
---|
122 |
|
---|
123 |
|
---|
124 | /**
|
---|
125 | * FAT directory.
|
---|
126 | *
|
---|
127 | * We work directories in one of two buffering modes. If there are few entries
|
---|
128 | * or if it's the FAT12/16 root directory, we map the whole thing into memory.
|
---|
129 | * If it's too large, we use an inefficient sector buffer for now.
|
---|
130 | *
|
---|
131 | * Directory entry updates happens exclusively via the directory, so any open
|
---|
132 | * files or subdirs have a parent reference for doing that. The parent OTOH,
|
---|
133 | * keeps a list of open children.
|
---|
134 | */
|
---|
135 | typedef struct RTFSFATDIR
|
---|
136 | {
|
---|
137 | /** Core FAT object info. */
|
---|
138 | RTFSFATOBJ Core;
|
---|
139 | /** Open child objects (RTFSFATOBJ). */
|
---|
140 | RTLISTNODE OpenChildren;
|
---|
141 |
|
---|
142 | /** Number of directory entries. */
|
---|
143 | uint32_t cEntries;
|
---|
144 |
|
---|
145 | /** If fully buffered. */
|
---|
146 | bool fFullyBuffered;
|
---|
147 | /** Set if this is a linear root directory. */
|
---|
148 | bool fIsFlatRootDir;
|
---|
149 |
|
---|
150 | union
|
---|
151 | {
|
---|
152 | /** Data for the full buffered mode.
|
---|
153 | * No need to messing around with clusters here, as we only uses this for
|
---|
154 | * directories with a contiguous mapping on the disk.
|
---|
155 | * So, if we grow a directory in a non-contiguous manner, we have to switch
|
---|
156 | * to sector buffering on the fly. */
|
---|
157 | struct
|
---|
158 | {
|
---|
159 | /** Directory offset. */
|
---|
160 | uint64_t offDir;
|
---|
161 | /** Number of sectors mapped by paEntries and pbDirtySectors. */
|
---|
162 | uint32_t cSectors;
|
---|
163 | /** Number of dirty sectors. */
|
---|
164 | uint32_t cDirtySectors;
|
---|
165 | /** Pointer to the linear mapping of the directory entries. */
|
---|
166 | PFATDIRENTRYUNION paEntries;
|
---|
167 | /** Dirty sector map. */
|
---|
168 | uint8_t *pbDirtySectors;
|
---|
169 | } Full;
|
---|
170 | /** The simple sector buffering.
|
---|
171 | * This only works for clusters, so no FAT12/16 root directory fun. */
|
---|
172 | struct
|
---|
173 | {
|
---|
174 | /** The disk offset of the current sector, UINT64_MAX if invalid. */
|
---|
175 | uint64_t offOnDisk;
|
---|
176 | /** The directory offset, UINT32_MAX if invalid. */
|
---|
177 | uint32_t offInDir;
|
---|
178 | uint32_t u32Reserved; /**< Puts pbSector and paEntries at the same location */
|
---|
179 | /** Sector buffer. */
|
---|
180 | PFATDIRENTRYUNION *paEntries;
|
---|
181 | /** Dirty flag. */
|
---|
182 | bool fDirty;
|
---|
183 | } Simple;
|
---|
184 | } u;
|
---|
185 | } RTFSFATDIR;
|
---|
186 | /** Pointer to a FAT directory instance. */
|
---|
187 | typedef RTFSFATDIR *PRTFSFATDIR;
|
---|
188 |
|
---|
189 |
|
---|
190 | /**
|
---|
191 | * File allocation table cache entry.
|
---|
192 | */
|
---|
193 | typedef struct RTFSFATCLUSTERMAPENTRY
|
---|
194 | {
|
---|
195 | /** The byte offset into the fat, UINT32_MAX if invalid entry. */
|
---|
196 | uint32_t offFat;
|
---|
197 | /** Pointer to the data. */
|
---|
198 | uint8_t *pbData;
|
---|
199 | /** Dirty bitmap. Indexed by byte offset right shifted by
|
---|
200 | * RTFSFATCLUSTERMAPCACHE::cDirtyShift. */
|
---|
201 | uint64_t bmDirty;
|
---|
202 | } RTFSFATCLUSTERMAPENTRY;
|
---|
203 | /** Pointer to a file allocation table cache entry. */
|
---|
204 | typedef RTFSFATCLUSTERMAPENTRY *PRTFSFATCLUSTERMAPENTRY;
|
---|
205 |
|
---|
206 | /**
|
---|
207 | * File allocation table cache.
|
---|
208 | */
|
---|
209 | typedef struct RTFSFATCLUSTERMAPCACHE
|
---|
210 | {
|
---|
211 | /** Number of cache entries. */
|
---|
212 | uint32_t cEntries;
|
---|
213 | /** The max size of data in a cache entry. */
|
---|
214 | uint32_t cbEntry;
|
---|
215 | /** Dirty bitmap shift count. */
|
---|
216 | uint32_t cDirtyShift;
|
---|
217 | /** The dirty cache line size (multiple of two). */
|
---|
218 | uint32_t cbDirtyLine;
|
---|
219 | /** The cache name. */
|
---|
220 | const char *pszName;
|
---|
221 | /** Cache entries. */
|
---|
222 | RTFSFATCLUSTERMAPENTRY aEntries[RT_FLEXIBLE_ARRAY];
|
---|
223 | } RTFSFATCLUSTERMAPCACHE;
|
---|
224 | /** Pointer to a FAT linear metadata cache. */
|
---|
225 | typedef RTFSFATCLUSTERMAPCACHE *PRTFSFATCLUSTERMAPCACHE;
|
---|
226 |
|
---|
227 |
|
---|
228 | /**
|
---|
229 | * FAT type (format).
|
---|
230 | */
|
---|
231 | typedef enum RTFSFATTYPE
|
---|
232 | {
|
---|
233 | RTFSFATTYPE_INVALID = 0,
|
---|
234 | RTFSFATTYPE_FAT12,
|
---|
235 | RTFSFATTYPE_FAT16,
|
---|
236 | RTFSFATTYPE_FAT32,
|
---|
237 | RTFSFATTYPE_END
|
---|
238 | } RTFSFATTYPE;
|
---|
239 |
|
---|
240 | /**
|
---|
241 | * A FAT volume.
|
---|
242 | */
|
---|
243 | typedef struct RTFSFATVOL
|
---|
244 | {
|
---|
245 | /** Handle to itself. */
|
---|
246 | RTVFS hVfsSelf;
|
---|
247 | /** The file, partition, or whatever backing the FAT volume. */
|
---|
248 | RTVFSFILE hVfsBacking;
|
---|
249 | /** The size of the backing thingy. */
|
---|
250 | uint64_t cbBacking;
|
---|
251 | /** Byte offset of the bootsector relative to the start of the file. */
|
---|
252 | uint64_t offBootSector;
|
---|
253 | /** Set if read-only mode. */
|
---|
254 | bool fReadOnly;
|
---|
255 | /** Media byte. */
|
---|
256 | uint8_t bMedia;
|
---|
257 | /** Reserved sectors. */
|
---|
258 | uint32_t cReservedSectors;
|
---|
259 |
|
---|
260 | /** Logical sector size. */
|
---|
261 | uint32_t cbSector;
|
---|
262 | /** The cluster size in bytes. */
|
---|
263 | uint32_t cbCluster;
|
---|
264 | /** The number of data clusters, including the two reserved ones. */
|
---|
265 | uint32_t cClusters;
|
---|
266 | /** The offset of the first cluster. */
|
---|
267 | uint64_t offFirstCluster;
|
---|
268 | /** The total size from the BPB, in bytes. */
|
---|
269 | uint64_t cbTotalSize;
|
---|
270 |
|
---|
271 | /** The FAT type. */
|
---|
272 | RTFSFATTYPE enmFatType;
|
---|
273 |
|
---|
274 | /** Number of FAT entries (clusters). */
|
---|
275 | uint32_t cFatEntries;
|
---|
276 | /** The size of a FAT, in bytes. */
|
---|
277 | uint32_t cbFat;
|
---|
278 | /** Number of FATs. */
|
---|
279 | uint32_t cFats;
|
---|
280 | /** The end of chain marker used by the formatter (FAT entry \#2). */
|
---|
281 | uint32_t idxEndOfChain;
|
---|
282 | /** The maximum last cluster supported by the FAT format. */
|
---|
283 | uint32_t idxMaxLastCluster;
|
---|
284 | /** FAT byte offsets. */
|
---|
285 | uint64_t aoffFats[8];
|
---|
286 | /** Pointer to the FAT (cluster map) cache. */
|
---|
287 | PRTFSFATCLUSTERMAPCACHE pFatCache;
|
---|
288 |
|
---|
289 | /** The root directory byte offset. */
|
---|
290 | uint64_t offRootDir;
|
---|
291 | /** Root directory cluster, UINT32_MAX if not FAT32. */
|
---|
292 | uint32_t idxRootDirCluster;
|
---|
293 | /** Number of root directory entries, if fixed. UINT32_MAX for FAT32. */
|
---|
294 | uint32_t cRootDirEntries;
|
---|
295 | /** The size of the root directory, rounded up to the nearest sector size. */
|
---|
296 | uint32_t cbRootDir;
|
---|
297 | /** The root directory handle. */
|
---|
298 | RTVFSDIR hVfsRootDir;
|
---|
299 | /** The root directory instance data. */
|
---|
300 | PRTFSFATDIR pRootDir;
|
---|
301 |
|
---|
302 | /** Serial number. */
|
---|
303 | uint32_t uSerialNo;
|
---|
304 | /** The stripped volume label, if included in EBPB. */
|
---|
305 | char szLabel[12];
|
---|
306 | /** The file system type from the EBPB (also stripped). */
|
---|
307 | char szType[9];
|
---|
308 | /** Number of FAT32 boot sector copies. */
|
---|
309 | uint8_t cBootSectorCopies;
|
---|
310 | /** FAT32 flags. */
|
---|
311 | uint16_t fFat32Flags;
|
---|
312 | /** Offset of the FAT32 boot sector copies, UINT64_MAX if none. */
|
---|
313 | uint64_t offBootSectorCopies;
|
---|
314 |
|
---|
315 | /** The FAT32 info sector byte offset, UINT64_MAX if not present. */
|
---|
316 | uint64_t offFat32InfoSector;
|
---|
317 | /** The FAT32 info sector if offFat32InfoSector isn't UINT64_MAX. */
|
---|
318 | FAT32INFOSECTOR Fat32InfoSector;
|
---|
319 | } RTFSFATVOL;
|
---|
320 | /** Pointer to a FAT volume (VFS instance data). */
|
---|
321 | typedef RTFSFATVOL *PRTFSFATVOL;
|
---|
322 |
|
---|
323 |
|
---|
324 |
|
---|
325 | /**
|
---|
326 | * Checks if the cluster chain is contiguous on the disk.
|
---|
327 | *
|
---|
328 | * @returns true / false.
|
---|
329 | * @param pChain The chain.
|
---|
330 | */
|
---|
331 | static bool rtFsFatChain_IsContiguous(PCRTFSFATCHAIN pChain)
|
---|
332 | {
|
---|
333 | if (pChain->cClusters <= 1)
|
---|
334 | return true;
|
---|
335 |
|
---|
336 | PRTFSFATCHAINPART pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
|
---|
337 | uint32_t idxNext = pPart->aEntries[0];
|
---|
338 | uint32_t cLeft = pChain->cClusters;
|
---|
339 | for (;;)
|
---|
340 | {
|
---|
341 | uint32_t const cInPart = RT_MIN(cLeft, RT_ELEMENTS(pPart->aEntries));
|
---|
342 | for (uint32_t iPart = 0; iPart < cInPart; iPart++)
|
---|
343 | if (pPart->aEntries[iPart] == idxNext)
|
---|
344 | idxNext++;
|
---|
345 | else
|
---|
346 | return false;
|
---|
347 | cLeft -= cInPart;
|
---|
348 | if (!cLeft)
|
---|
349 | return true;
|
---|
350 | pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
|
---|
351 | }
|
---|
352 | }
|
---|
353 |
|
---|
354 |
|
---|
355 |
|
---|
356 | /**
|
---|
357 | * Creates a cache for the file allocation table (cluster map).
|
---|
358 | *
|
---|
359 | * @returns Pointer to the cache.
|
---|
360 | * @param pThis The FAT volume instance.
|
---|
361 | * @param pbFirst512FatBytes The first 512 bytes of the first FAT.
|
---|
362 | */
|
---|
363 | static int rtFsFatClusterMap_Create(PRTFSFATVOL pThis, uint8_t const *pbFirst512FatBytes, PRTERRINFO pErrInfo)
|
---|
364 | {
|
---|
365 | Assert(RT_ALIGN_32(pThis->cbFat, pThis->cbSector) == pThis->cbFat);
|
---|
366 | Assert(pThis->cbFat != 0);
|
---|
367 |
|
---|
368 | /*
|
---|
369 | * Figure the cache size. Keeping it _very_ simple for now as we just need
|
---|
370 | * something that works, not anything the performs like crazy.
|
---|
371 | */
|
---|
372 | uint32_t cEntries;
|
---|
373 | uint32_t cbEntry = pThis->cbFat;
|
---|
374 | if (cbEntry <= _512K)
|
---|
375 | cEntries = 1;
|
---|
376 | else
|
---|
377 | {
|
---|
378 | Assert(pThis->cbSector < _512K / 8);
|
---|
379 | cEntries = 8;
|
---|
380 | cbEntry = pThis->cbSector;
|
---|
381 | }
|
---|
382 |
|
---|
383 | /*
|
---|
384 | * Allocate and initialize it all.
|
---|
385 | */
|
---|
386 | PRTFSFATCLUSTERMAPCACHE pCache;
|
---|
387 | pThis->pFatCache = pCache = (PRTFSFATCLUSTERMAPCACHE)RTMemAllocZ(RT_OFFSETOF(RTFSFATCLUSTERMAPCACHE, aEntries[cEntries]));
|
---|
388 | if (!pCache)
|
---|
389 | return RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "Failed to allocate FAT cache");
|
---|
390 | pCache->cEntries = cEntries;
|
---|
391 | pCache->cbEntry = cbEntry;
|
---|
392 |
|
---|
393 | unsigned i = cEntries;
|
---|
394 | while (i-- > 0)
|
---|
395 | {
|
---|
396 | pCache->aEntries[i].pbData = (uint8_t *)RTMemAlloc(cbEntry);
|
---|
397 | if (pCache->aEntries[i].pbData == NULL)
|
---|
398 | {
|
---|
399 | for (i++; i < cEntries; i++)
|
---|
400 | RTMemFree(pCache->aEntries[i].pbData);
|
---|
401 | RTMemFree(pCache);
|
---|
402 | return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate FAT cache entry (%#x bytes)", cbEntry);
|
---|
403 | }
|
---|
404 |
|
---|
405 | pCache->aEntries[i].offFat = UINT32_MAX;
|
---|
406 | pCache->aEntries[i].bmDirty = 0;
|
---|
407 | }
|
---|
408 |
|
---|
409 | /*
|
---|
410 | * Calc the dirty shift factor.
|
---|
411 | */
|
---|
412 | cbEntry /= 64;
|
---|
413 | if (cbEntry < pThis->cbSector)
|
---|
414 | cbEntry = pThis->cbSector;
|
---|
415 |
|
---|
416 | pCache->cDirtyShift = 1;
|
---|
417 | pCache->cbDirtyLine = 1;
|
---|
418 | while (pCache->cbDirtyLine < cbEntry)
|
---|
419 | {
|
---|
420 | pCache->cDirtyShift++;
|
---|
421 | pCache->cbDirtyLine <<= 1;
|
---|
422 | }
|
---|
423 |
|
---|
424 | /*
|
---|
425 | * Fill the cache if single entry or entry size is 512.
|
---|
426 | */
|
---|
427 | if (pCache->cEntries == 1 || pCache->cbEntry == 512)
|
---|
428 | {
|
---|
429 | memcpy(pCache->aEntries[0].pbData, pbFirst512FatBytes, RT_MIN(512, pCache->cbEntry));
|
---|
430 | if (pCache->cbEntry > 512)
|
---|
431 | {
|
---|
432 | int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0] + 512,
|
---|
433 | &pCache->aEntries[0].pbData[512], pCache->cbEntry - 512, NULL);
|
---|
434 | if (RT_FAILURE(rc))
|
---|
435 | return RTErrInfoSet(pErrInfo, rc, "Error reading FAT into memory");
|
---|
436 | }
|
---|
437 | pCache->aEntries[0].offFat = 0;
|
---|
438 | pCache->aEntries[0].bmDirty = 0;
|
---|
439 | }
|
---|
440 |
|
---|
441 | return VINF_SUCCESS;
|
---|
442 | }
|
---|
443 |
|
---|
444 |
|
---|
445 | /**
|
---|
446 | * Worker for rtFsFatClusterMap_Flush and rtFsFatClusterMap_FlushEntry.
|
---|
447 | *
|
---|
448 | * @returns IPRT status code. On failure, we're currently kind of screwed.
|
---|
449 | * @param pThis The FAT volume instance.
|
---|
450 | * @param iFirstEntry Entry to start flushing at.
|
---|
451 | * @param iLastEntry Last entry to flush.
|
---|
452 | */
|
---|
453 | static int rtFsFatClusterMap_FlushWorker(PRTFSFATVOL pThis, uint32_t const iFirstEntry, uint32_t const iLastEntry)
|
---|
454 | {
|
---|
455 | PRTFSFATCLUSTERMAPCACHE pCache = pThis->pFatCache;
|
---|
456 |
|
---|
457 | /*
|
---|
458 | * Walk the cache entries, accumulating segments to flush.
|
---|
459 | */
|
---|
460 | int rc = VINF_SUCCESS;
|
---|
461 | uint64_t off = UINT64_MAX;
|
---|
462 | uint64_t offEdge = UINT64_MAX;
|
---|
463 | RTSGSEG aSgSegs[8];
|
---|
464 | RTSGBUF SgBuf;
|
---|
465 | RTSgBufInit(&SgBuf, aSgSegs, RT_ELEMENTS(aSgSegs));
|
---|
466 | SgBuf.cSegs = 0; /** @todo RTSgBuf API is stupid, make it smarter. */
|
---|
467 |
|
---|
468 | for (uint32_t iFatCopy = 0; iFatCopy < pThis->cFats; iFatCopy++)
|
---|
469 | {
|
---|
470 | for (uint32_t iEntry = iFirstEntry; iEntry <= iLastEntry; iEntry++)
|
---|
471 | {
|
---|
472 | uint64_t bmDirty = pCache->aEntries[iEntry].bmDirty;
|
---|
473 | if ( bmDirty != 0
|
---|
474 | && pCache->aEntries[iEntry].offFat != UINT32_MAX)
|
---|
475 | {
|
---|
476 | uint32_t offEntry = 0;
|
---|
477 | uint64_t iDirtyLine = 1;
|
---|
478 | while (offEntry < pCache->cbEntry)
|
---|
479 | {
|
---|
480 | if (pCache->aEntries[iEntry].bmDirty & iDirtyLine)
|
---|
481 | {
|
---|
482 | /*
|
---|
483 | * Found dirty cache line.
|
---|
484 | */
|
---|
485 | uint64_t offDirtyLine = pThis->aoffFats[iFatCopy] + pCache->aEntries[iEntry].offFat + offEntry;
|
---|
486 |
|
---|
487 | /* Can we simply extend the last segment? */
|
---|
488 | if ( offDirtyLine == offEdge
|
---|
489 | && offEntry)
|
---|
490 | {
|
---|
491 | Assert( (uintptr_t)aSgSegs[SgBuf.cSegs - 1].pvSeg + aSgSegs[SgBuf.cSegs - 1].cbSeg
|
---|
492 | == (uintptr_t)&pCache->aEntries[iEntry].pbData[offEntry]);
|
---|
493 | aSgSegs[SgBuf.cSegs - 1].cbSeg += pCache->cbDirtyLine;
|
---|
494 | offEdge += pCache->cbDirtyLine;
|
---|
495 | }
|
---|
496 | else
|
---|
497 | {
|
---|
498 | /* flush if not adjacent or if we're out of segments. */
|
---|
499 | if ( offDirtyLine != offEdge
|
---|
500 | || SgBuf.cSegs >= RT_ELEMENTS(aSgSegs))
|
---|
501 | {
|
---|
502 | int rc2 = RTVfsFileSgWrite(pThis->hVfsBacking, off, &SgBuf, true /*fBlocking*/, NULL);
|
---|
503 | if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
|
---|
504 | rc = rc2;
|
---|
505 | RTSgBufReset(&SgBuf);
|
---|
506 | SgBuf.cSegs = 0;
|
---|
507 | }
|
---|
508 |
|
---|
509 | /* Append segment. */
|
---|
510 | aSgSegs[SgBuf.cSegs].cbSeg = pCache->cbDirtyLine;
|
---|
511 | aSgSegs[SgBuf.cSegs].pvSeg = &pCache->aEntries[iEntry].pbData[offEntry];
|
---|
512 | SgBuf.cSegs++;
|
---|
513 | offEdge = offDirtyLine + pCache->cbDirtyLine;
|
---|
514 | }
|
---|
515 |
|
---|
516 | bmDirty &= ~iDirtyLine;
|
---|
517 | if (!bmDirty)
|
---|
518 | break;
|
---|
519 | }
|
---|
520 | }
|
---|
521 | iDirtyLine++;
|
---|
522 | offEntry += pCache->cbDirtyLine;
|
---|
523 | }
|
---|
524 | }
|
---|
525 | }
|
---|
526 |
|
---|
527 | /*
|
---|
528 | * Final flush job.
|
---|
529 | */
|
---|
530 | if (SgBuf.cSegs > 0)
|
---|
531 | {
|
---|
532 | int rc2 = RTVfsFileSgWrite(pThis->hVfsBacking, off, &SgBuf, true /*fBlocking*/, NULL);
|
---|
533 | if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
|
---|
534 | rc = rc2;
|
---|
535 | }
|
---|
536 |
|
---|
537 | /*
|
---|
538 | * Clear the dirty flags on success.
|
---|
539 | */
|
---|
540 | if (RT_SUCCESS(rc))
|
---|
541 | for (uint32_t iEntry = iFirstEntry; iEntry <= iLastEntry; iEntry++)
|
---|
542 | pCache->aEntries[iEntry].bmDirty = 0;
|
---|
543 |
|
---|
544 | return rc;
|
---|
545 | }
|
---|
546 |
|
---|
547 |
|
---|
548 | /**
|
---|
549 | * Flushes out all dirty lines in the entire file allocation table cache.
|
---|
550 | *
|
---|
551 | * @returns IPRT status code. On failure, we're currently kind of screwed.
|
---|
552 | * @param pThis The FAT volume instance.
|
---|
553 | */
|
---|
554 | static int rtFsFatClusterMap_Flush(PRTFSFATVOL pThis)
|
---|
555 | {
|
---|
556 | return rtFsFatClusterMap_FlushWorker(pThis, 0, pThis->pFatCache->cEntries - 1);
|
---|
557 | }
|
---|
558 |
|
---|
559 |
|
---|
560 | /**
|
---|
561 | * Flushes out all dirty lines in the file allocation table (cluster map) cache.
|
---|
562 | *
|
---|
563 | * This is typically called prior to reusing the cache entry.
|
---|
564 | *
|
---|
565 | * @returns IPRT status code. On failure, we're currently kind of screwed.
|
---|
566 | * @param pThis The FAT volume instance.
|
---|
567 | * @param iEntry The cache entry to flush.
|
---|
568 | */
|
---|
569 | static int rtFsFatClusterMap_FlushEntry(PRTFSFATVOL pThis, uint32_t iEntry)
|
---|
570 | {
|
---|
571 | return rtFsFatClusterMap_FlushWorker(pThis, iEntry, iEntry);
|
---|
572 | }
|
---|
573 |
|
---|
574 |
|
---|
575 | /**
|
---|
576 | * Destroys the file allcation table cache, first flushing any dirty lines.
|
---|
577 | *
|
---|
578 | * @returns IRPT status code from flush (we've destroyed it regardless of the
|
---|
579 | * status code).
|
---|
580 | * @param pThis The FAT volume instance which cluster map shall be
|
---|
581 | * destroyed.
|
---|
582 | */
|
---|
583 | static int rtFsFatClusterMap_Destroy(PRTFSFATVOL pThis)
|
---|
584 | {
|
---|
585 | int rc = VINF_SUCCESS;
|
---|
586 | PRTFSFATCLUSTERMAPCACHE pCache = pThis->pFatCache;
|
---|
587 | if (pCache)
|
---|
588 | {
|
---|
589 | /* flush stuff. */
|
---|
590 | rc = rtFsFatClusterMap_Flush(pThis);
|
---|
591 |
|
---|
592 | /* free everything. */
|
---|
593 | uint32_t i = pCache->cEntries;
|
---|
594 | while (i-- > 0)
|
---|
595 | {
|
---|
596 | RTMemFree(pCache->aEntries[i].pbData);
|
---|
597 | pCache->aEntries[i].pbData = NULL;
|
---|
598 | }
|
---|
599 | pCache->cEntries = 0;
|
---|
600 | RTMemFree(pCache);
|
---|
601 |
|
---|
602 | pThis->pFatCache = NULL;
|
---|
603 | }
|
---|
604 |
|
---|
605 | return rc;
|
---|
606 | }
|
---|
607 |
|
---|
608 |
|
---|
609 | /**
|
---|
610 | * Reads a cluster chain into memory
|
---|
611 | *
|
---|
612 | * @returns IPRT status code.
|
---|
613 | * @param pThis The FAT volume instance.
|
---|
614 | * @param idxFirstCluster The first cluster.
|
---|
615 | * @param pChain The chain element to read into (and thereby
|
---|
616 | * initialize).
|
---|
617 | */
|
---|
618 | static int rtFsFatClusterMap_ReadClusterChain(PRTFSFATVOL pThis, uint32_t idxFirstCluster, PRTFSFATCHAIN pChain)
|
---|
619 | {
|
---|
620 | pChain->cClusters = 0;
|
---|
621 | pChain->cbChain = 0;
|
---|
622 | RTListInit(&pChain->ListParts);
|
---|
623 |
|
---|
624 | if ( idxFirstCluster >= pThis->cClusters
|
---|
625 | && idxFirstCluster >= FAT_FIRST_DATA_CLUSTER)
|
---|
626 | return VERR_VFS_BOGUS_OFFSET;
|
---|
627 |
|
---|
628 | return VERR_NOT_IMPLEMENTED;
|
---|
629 | }
|
---|
630 |
|
---|
631 |
|
---|
632 | static void rtFsFatDosDateTimeToSpec(PRTTIMESPEC pTimeSpec, uint16_t uDate, uint16_t uTime, uint8_t cCentiseconds)
|
---|
633 | {
|
---|
634 | RTTIME Time;
|
---|
635 | Time.i32Year = 1980 + (uDate >> 9);
|
---|
636 | Time.u8Month = ((uDate >> 5) & 0xf);
|
---|
637 | Time.u8WeekDay = UINT8_MAX;
|
---|
638 | Time.u16YearDay = UINT16_MAX;
|
---|
639 | Time.u8MonthDay = RT_MAX(uDate & 0x1f, 1);
|
---|
640 | Time.u8Hour = uTime >> 11;
|
---|
641 | Time.u8Minute = (uTime >> 5) & 0x3f;
|
---|
642 | Time.u8Second = (uTime & 0x1f) << 1;
|
---|
643 | if (cCentiseconds > 0 && cCentiseconds < 200) /* screw complicated stuff for now. */
|
---|
644 | {
|
---|
645 | if (cCentiseconds >= 100)
|
---|
646 | {
|
---|
647 | cCentiseconds -= 100;
|
---|
648 | Time.u8Second++;
|
---|
649 | }
|
---|
650 | Time.u32Nanosecond = cCentiseconds * UINT64_C(100000000);
|
---|
651 | }
|
---|
652 |
|
---|
653 | RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
|
---|
654 | }
|
---|
655 |
|
---|
656 |
|
---|
657 | static void rtFsFatObj_InitFromDirEntry(PRTFSFATOBJ pObj, PCFATDIRENTRY pDirEntry, uint64_t offDirEntry, PRTFSFATVOL pThis)
|
---|
658 | {
|
---|
659 | RTListInit(&pObj->Entry);
|
---|
660 | pObj->pVol = pThis;
|
---|
661 | pObj->offDirEntry = offDirEntry;
|
---|
662 | pObj->fAttrib = ((RTFMODE)pDirEntry->fAttrib << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
|
---|
663 | pObj->cbObject = pDirEntry->cbFile;
|
---|
664 | rtFsFatDosDateTimeToSpec(&pObj->ModificationTime, pDirEntry->uAccessDate, pDirEntry->uModifyTime, 0);
|
---|
665 | rtFsFatDosDateTimeToSpec(&pObj->BirthTime, pDirEntry->uBirthDate, pDirEntry->uBirthTime, pDirEntry->uBirthCentiseconds);
|
---|
666 | rtFsFatDosDateTimeToSpec(&pObj->AccessTime, pDirEntry->uAccessDate, 0, 0);
|
---|
667 | }
|
---|
668 |
|
---|
669 |
|
---|
670 | static void rtFsFatObj_InitDummy(PRTFSFATOBJ pObj, uint64_t offDirEntry, uint32_t cbObject, RTFMODE fAttrib, PRTFSFATVOL pThis)
|
---|
671 | {
|
---|
672 | RTListInit(&pObj->Entry);
|
---|
673 | pObj->pVol = pThis;
|
---|
674 | pObj->offDirEntry = offDirEntry;
|
---|
675 | pObj->fAttrib = fAttrib;
|
---|
676 | pObj->cbObject = cbObject;
|
---|
677 | RTTimeSpecSetDosSeconds(&pObj->AccessTime, 0);
|
---|
678 | RTTimeSpecSetDosSeconds(&pObj->ModificationTime, 0);
|
---|
679 | RTTimeSpecSetDosSeconds(&pObj->BirthTime, 0);
|
---|
680 | }
|
---|
681 |
|
---|
682 |
|
---|
683 | /**
|
---|
684 | * @interface_method_impl{RTVFSOBJOPS,pfnClose}
|
---|
685 | */
|
---|
686 | static DECLCALLBACK(int) rtFsFatFile_Close(void *pvThis)
|
---|
687 | {
|
---|
688 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
689 | RT_NOREF(pThis);
|
---|
690 | return VERR_NOT_IMPLEMENTED;
|
---|
691 | }
|
---|
692 |
|
---|
693 |
|
---|
694 | /**
|
---|
695 | * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
|
---|
696 | */
|
---|
697 | static DECLCALLBACK(int) rtFsFatObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
|
---|
698 | {
|
---|
699 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
700 |
|
---|
701 | pObjInfo->cbObject = pThis->Core.cbObject;
|
---|
702 | pObjInfo->cbAllocated = pThis->Core.Clusters.cClusters * pThis->Core.pVol->cbCluster;
|
---|
703 | pObjInfo->AccessTime = pThis->Core.AccessTime;
|
---|
704 | pObjInfo->ModificationTime = pThis->Core.ModificationTime;
|
---|
705 | pObjInfo->ChangeTime = pThis->Core.ModificationTime;
|
---|
706 | pObjInfo->BirthTime = pThis->Core.BirthTime;
|
---|
707 | pObjInfo->Attr.fMode = pThis->Core.fAttrib;
|
---|
708 | pObjInfo->Attr.enmAdditional = enmAddAttr;
|
---|
709 |
|
---|
710 | switch (enmAddAttr)
|
---|
711 | {
|
---|
712 | case RTFSOBJATTRADD_NOTHING: /* fall thru */
|
---|
713 | case RTFSOBJATTRADD_UNIX:
|
---|
714 | pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
|
---|
715 | pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
|
---|
716 | pObjInfo->Attr.u.Unix.cHardlinks = 1;
|
---|
717 | pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
|
---|
718 | pObjInfo->Attr.u.Unix.INodeId = 0; /* Could probably use the directory entry offset. */
|
---|
719 | pObjInfo->Attr.u.Unix.fFlags = 0;
|
---|
720 | pObjInfo->Attr.u.Unix.GenerationId = 0;
|
---|
721 | pObjInfo->Attr.u.Unix.Device = 0;
|
---|
722 | break;
|
---|
723 | case RTFSOBJATTRADD_UNIX_OWNER:
|
---|
724 | pObjInfo->Attr.u.UnixOwner.uid = 0;
|
---|
725 | pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
|
---|
726 | break;
|
---|
727 | case RTFSOBJATTRADD_UNIX_GROUP:
|
---|
728 | pObjInfo->Attr.u.UnixGroup.gid = 0;
|
---|
729 | pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
|
---|
730 | break;
|
---|
731 | case RTFSOBJATTRADD_EASIZE:
|
---|
732 | pObjInfo->Attr.u.EASize.cb = 0;
|
---|
733 | break;
|
---|
734 | default:
|
---|
735 | return VERR_INVALID_PARAMETER;
|
---|
736 | }
|
---|
737 | return VINF_SUCCESS;
|
---|
738 | }
|
---|
739 |
|
---|
740 |
|
---|
741 | /**
|
---|
742 | * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
|
---|
743 | */
|
---|
744 | static DECLCALLBACK(int) rtFsFatFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
|
---|
745 | {
|
---|
746 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
747 | RT_NOREF(pThis, off, pSgBuf, fBlocking, pcbRead);
|
---|
748 | return VERR_NOT_IMPLEMENTED;
|
---|
749 | }
|
---|
750 |
|
---|
751 |
|
---|
752 | /**
|
---|
753 | * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
|
---|
754 | */
|
---|
755 | static DECLCALLBACK(int) rtFsFatFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
|
---|
756 | {
|
---|
757 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
758 | RT_NOREF(pThis, off, pSgBuf, fBlocking, pcbWritten);
|
---|
759 | return VERR_NOT_IMPLEMENTED;
|
---|
760 | }
|
---|
761 |
|
---|
762 |
|
---|
763 | /**
|
---|
764 | * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
|
---|
765 | */
|
---|
766 | static DECLCALLBACK(int) rtFsFatFile_Flush(void *pvThis)
|
---|
767 | {
|
---|
768 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
769 | RT_NOREF(pThis);
|
---|
770 | return VERR_NOT_IMPLEMENTED;
|
---|
771 | }
|
---|
772 |
|
---|
773 |
|
---|
774 | /**
|
---|
775 | * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
|
---|
776 | */
|
---|
777 | static DECLCALLBACK(int) rtFsFatFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
|
---|
778 | uint32_t *pfRetEvents)
|
---|
779 | {
|
---|
780 | NOREF(pvThis);
|
---|
781 | int rc;
|
---|
782 | if (fEvents != RTPOLL_EVT_ERROR)
|
---|
783 | {
|
---|
784 | *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
|
---|
785 | rc = VINF_SUCCESS;
|
---|
786 | }
|
---|
787 | else if (fIntr)
|
---|
788 | rc = RTThreadSleep(cMillies);
|
---|
789 | else
|
---|
790 | {
|
---|
791 | uint64_t uMsStart = RTTimeMilliTS();
|
---|
792 | do
|
---|
793 | rc = RTThreadSleep(cMillies);
|
---|
794 | while ( rc == VERR_INTERRUPTED
|
---|
795 | && !fIntr
|
---|
796 | && RTTimeMilliTS() - uMsStart < cMillies);
|
---|
797 | if (rc == VERR_INTERRUPTED)
|
---|
798 | rc = VERR_TIMEOUT;
|
---|
799 | }
|
---|
800 | return rc;
|
---|
801 | }
|
---|
802 |
|
---|
803 |
|
---|
804 | /**
|
---|
805 | * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
|
---|
806 | */
|
---|
807 | static DECLCALLBACK(int) rtFsFatFile_Tell(void *pvThis, PRTFOFF poffActual)
|
---|
808 | {
|
---|
809 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
810 | *poffActual = pThis->offFile;
|
---|
811 | return VINF_SUCCESS;
|
---|
812 | }
|
---|
813 |
|
---|
814 |
|
---|
815 | /**
|
---|
816 | * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
|
---|
817 | */
|
---|
818 | static DECLCALLBACK(int) rtFsFatObj_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
|
---|
819 | {
|
---|
820 | #if 0
|
---|
821 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
822 | if (fMask != ~RTFS_TYPE_MASK)
|
---|
823 | {
|
---|
824 | fMode |= ~fMask & ObjInfo.Attr.fMode;
|
---|
825 | }
|
---|
826 | #else
|
---|
827 | RT_NOREF(pvThis, fMode, fMask);
|
---|
828 | return VERR_NOT_IMPLEMENTED;
|
---|
829 | #endif
|
---|
830 | }
|
---|
831 |
|
---|
832 |
|
---|
833 | /**
|
---|
834 | * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
|
---|
835 | */
|
---|
836 | static DECLCALLBACK(int) rtFsFatObj_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
|
---|
837 | PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
|
---|
838 | {
|
---|
839 | #if 0
|
---|
840 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
841 | #else
|
---|
842 | RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
|
---|
843 | return VERR_NOT_IMPLEMENTED;
|
---|
844 | #endif
|
---|
845 | }
|
---|
846 |
|
---|
847 |
|
---|
848 | /**
|
---|
849 | * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
|
---|
850 | */
|
---|
851 | static DECLCALLBACK(int) rtFsFatObj_SetOwner(void *pvThis, RTUID uid, RTGID gid)
|
---|
852 | {
|
---|
853 | RT_NOREF(pvThis, uid, gid);
|
---|
854 | return VERR_NOT_SUPPORTED;
|
---|
855 | }
|
---|
856 |
|
---|
857 |
|
---|
858 | /**
|
---|
859 | * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
|
---|
860 | */
|
---|
861 | static DECLCALLBACK(int) rtFsFatFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
|
---|
862 | {
|
---|
863 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
864 | RTFOFF offNew;
|
---|
865 | switch (uMethod)
|
---|
866 | {
|
---|
867 | case RTFILE_SEEK_BEGIN:
|
---|
868 | offNew = offSeek;
|
---|
869 | break;
|
---|
870 | case RTFILE_SEEK_END:
|
---|
871 | offNew = (RTFOFF)pThis->Core.cbObject + offSeek;
|
---|
872 | break;
|
---|
873 | case RTFILE_SEEK_CURRENT:
|
---|
874 | offNew = (RTFOFF)pThis->offFile + offSeek;
|
---|
875 | break;
|
---|
876 | default:
|
---|
877 | return VERR_INVALID_PARAMETER;
|
---|
878 | }
|
---|
879 | if (offNew >= 0)
|
---|
880 | {
|
---|
881 | if (offNew <= _4G)
|
---|
882 | {
|
---|
883 | pThis->offFile = offNew;
|
---|
884 | *poffActual = offNew;
|
---|
885 | return VINF_SUCCESS;
|
---|
886 | }
|
---|
887 | return VERR_OUT_OF_RANGE;
|
---|
888 | }
|
---|
889 | return VERR_NEGATIVE_SEEK;
|
---|
890 | }
|
---|
891 |
|
---|
892 |
|
---|
893 | /**
|
---|
894 | * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
|
---|
895 | */
|
---|
896 | static DECLCALLBACK(int) rtFsFatFile_QuerySize(void *pvThis, uint64_t *pcbFile)
|
---|
897 | {
|
---|
898 | PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
|
---|
899 | *pcbFile = pThis->Core.cbObject;
|
---|
900 | return VINF_SUCCESS;
|
---|
901 | }
|
---|
902 |
|
---|
903 |
|
---|
904 | /**
|
---|
905 | * FAT file operations.
|
---|
906 | */
|
---|
907 | DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsFatFileOps =
|
---|
908 | {
|
---|
909 | { /* Stream */
|
---|
910 | { /* Obj */
|
---|
911 | RTVFSOBJOPS_VERSION,
|
---|
912 | RTVFSOBJTYPE_FILE,
|
---|
913 | "FatFile",
|
---|
914 | rtFsFatFile_Close,
|
---|
915 | rtFsFatObj_QueryInfo,
|
---|
916 | RTVFSOBJOPS_VERSION
|
---|
917 | },
|
---|
918 | RTVFSIOSTREAMOPS_VERSION,
|
---|
919 | 0,
|
---|
920 | rtFsFatFile_Read,
|
---|
921 | rtFsFatFile_Write,
|
---|
922 | rtFsFatFile_Flush,
|
---|
923 | rtFsFatFile_PollOne,
|
---|
924 | rtFsFatFile_Tell,
|
---|
925 | NULL /*pfnSkip*/,
|
---|
926 | NULL /*pfnZeroFill*/,
|
---|
927 | RTVFSIOSTREAMOPS_VERSION,
|
---|
928 | },
|
---|
929 | RTVFSFILEOPS_VERSION,
|
---|
930 | 0,
|
---|
931 | { /* ObjSet */
|
---|
932 | RTVFSOBJSETOPS_VERSION,
|
---|
933 | RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
|
---|
934 | rtFsFatObj_SetMode,
|
---|
935 | rtFsFatObj_SetTimes,
|
---|
936 | rtFsFatObj_SetOwner,
|
---|
937 | RTVFSOBJSETOPS_VERSION
|
---|
938 | },
|
---|
939 | rtFsFatFile_Seek,
|
---|
940 | rtFsFatFile_QuerySize,
|
---|
941 | RTVFSFILEOPS_VERSION
|
---|
942 | };
|
---|
943 |
|
---|
944 |
|
---|
945 | /**
|
---|
946 | * @interface_method_impl{RTVFSOBJOPS,pfnClose}
|
---|
947 | */
|
---|
948 | static DECLCALLBACK(int) rtFsFatDir_Close(void *pvThis)
|
---|
949 | {
|
---|
950 | PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
|
---|
951 | RT_NOREF(pThis);
|
---|
952 | return VERR_NOT_IMPLEMENTED;
|
---|
953 | }
|
---|
954 |
|
---|
955 |
|
---|
956 | /**
|
---|
957 | * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
|
---|
958 | */
|
---|
959 | static DECLCALLBACK(int) rtFsFatDir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
|
---|
960 | PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
|
---|
961 | {
|
---|
962 | RT_NOREF(pvThis, pszEntry, phVfsDir, phVfsSymlink, phVfsMounted);
|
---|
963 | return VERR_NOT_IMPLEMENTED;
|
---|
964 | }
|
---|
965 |
|
---|
966 |
|
---|
967 | /**
|
---|
968 | * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
|
---|
969 | */
|
---|
970 | static DECLCALLBACK(int) rtFsFatDir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
|
---|
971 | {
|
---|
972 | RT_NOREF(pvThis, pszFilename, fOpen, phVfsFile);
|
---|
973 | return VERR_NOT_IMPLEMENTED;
|
---|
974 | }
|
---|
975 |
|
---|
976 |
|
---|
977 | /**
|
---|
978 | * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
|
---|
979 | */
|
---|
980 | static DECLCALLBACK(int) rtFsFatDir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
|
---|
981 | {
|
---|
982 | RT_NOREF(pvThis, pszSubDir, fFlags, phVfsDir);
|
---|
983 | return VERR_NOT_IMPLEMENTED;
|
---|
984 | }
|
---|
985 |
|
---|
986 |
|
---|
987 | /**
|
---|
988 | * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
|
---|
989 | */
|
---|
990 | static DECLCALLBACK(int) rtFsFatDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
|
---|
991 | {
|
---|
992 | RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
|
---|
993 | return VERR_NOT_IMPLEMENTED;
|
---|
994 | }
|
---|
995 |
|
---|
996 |
|
---|
997 | /**
|
---|
998 | * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
|
---|
999 | */
|
---|
1000 | static DECLCALLBACK(int) rtFsFatDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
|
---|
1001 | {
|
---|
1002 | RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
|
---|
1003 | return VERR_NOT_SUPPORTED;
|
---|
1004 | }
|
---|
1005 |
|
---|
1006 |
|
---|
1007 | /**
|
---|
1008 | * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
|
---|
1009 | */
|
---|
1010 | static DECLCALLBACK(int) rtFsFatDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
|
---|
1011 | RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
|
---|
1012 | {
|
---|
1013 | RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
|
---|
1014 | return VERR_NOT_SUPPORTED;
|
---|
1015 | }
|
---|
1016 |
|
---|
1017 |
|
---|
1018 | /**
|
---|
1019 | * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
|
---|
1020 | */
|
---|
1021 | static DECLCALLBACK(int) rtFsFatDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
|
---|
1022 | {
|
---|
1023 | RT_NOREF(pvThis, pszEntry, fType);
|
---|
1024 | return VERR_NOT_IMPLEMENTED;
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 |
|
---|
1028 | /**
|
---|
1029 | * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
|
---|
1030 | */
|
---|
1031 | static DECLCALLBACK(int) rtFsFatDir_RewindDir(void *pvThis)
|
---|
1032 | {
|
---|
1033 | RT_NOREF(pvThis);
|
---|
1034 | return VERR_NOT_IMPLEMENTED;
|
---|
1035 | }
|
---|
1036 |
|
---|
1037 |
|
---|
1038 | /**
|
---|
1039 | * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
|
---|
1040 | */
|
---|
1041 | static DECLCALLBACK(int) rtFsFatDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
|
---|
1042 | RTFSOBJATTRADD enmAddAttr)
|
---|
1043 | {
|
---|
1044 | RT_NOREF(pvThis, pDirEntry, pcbDirEntry, enmAddAttr);
|
---|
1045 | return VERR_NOT_IMPLEMENTED;
|
---|
1046 | }
|
---|
1047 |
|
---|
1048 |
|
---|
1049 | /**
|
---|
1050 | * FAT file operations.
|
---|
1051 | */
|
---|
1052 | static const RTVFSDIROPS g_rtFsFatDirOps =
|
---|
1053 | {
|
---|
1054 | { /* Obj */
|
---|
1055 | RTVFSOBJOPS_VERSION,
|
---|
1056 | RTVFSOBJTYPE_FILE,
|
---|
1057 | "FatDir",
|
---|
1058 | rtFsFatDir_Close,
|
---|
1059 | rtFsFatObj_QueryInfo,
|
---|
1060 | RTVFSOBJOPS_VERSION
|
---|
1061 | },
|
---|
1062 | RTVFSDIROPS_VERSION,
|
---|
1063 | 0,
|
---|
1064 | { /* ObjSet */
|
---|
1065 | RTVFSOBJSETOPS_VERSION,
|
---|
1066 | RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
|
---|
1067 | rtFsFatObj_SetMode,
|
---|
1068 | rtFsFatObj_SetTimes,
|
---|
1069 | rtFsFatObj_SetOwner,
|
---|
1070 | RTVFSOBJSETOPS_VERSION
|
---|
1071 | },
|
---|
1072 | rtFsFatDir_TraversalOpen,
|
---|
1073 | rtFsFatDir_OpenFile,
|
---|
1074 | rtFsFatDir_OpenDir,
|
---|
1075 | rtFsFatDir_CreateDir,
|
---|
1076 | rtFsFatDir_OpenSymlink,
|
---|
1077 | rtFsFatDir_CreateSymlink,
|
---|
1078 | rtFsFatDir_UnlinkEntry,
|
---|
1079 | rtFsFatDir_RewindDir,
|
---|
1080 | rtFsFatDir_ReadDir,
|
---|
1081 | RTVFSDIROPS_VERSION,
|
---|
1082 | };
|
---|
1083 |
|
---|
1084 |
|
---|
1085 | /**
|
---|
1086 | * Instantiates a new directory.
|
---|
1087 | *
|
---|
1088 | * @returns IPRT status code.
|
---|
1089 | * @param pThis The FAT volume instance.
|
---|
1090 | * @param pParentDir The parent directory. This is NULL for the root
|
---|
1091 | * directory.
|
---|
1092 | * @param pDirEntry The parent directory entry. This is NULL for the root
|
---|
1093 | * directory.
|
---|
1094 | * @param offDirEntry The byte offset of the directory entry. UINT64_MAX if
|
---|
1095 | * root directory.
|
---|
1096 | * @param idxCluster The cluster where the directory content is to be found.
|
---|
1097 | * This can be UINT32_MAX if a root FAT12/16 directory.
|
---|
1098 | * @param offDisk The disk byte offset of the FAT12/16 root directory.
|
---|
1099 | * This is UINT64_MAX if idxCluster is given.
|
---|
1100 | * @param cbDir The size of the directory.
|
---|
1101 | * @param phVfsDir Where to return the directory handle.
|
---|
1102 | * @param ppDir Where to return the FAT directory instance data.
|
---|
1103 | */
|
---|
1104 | static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint64_t offDirEntry,
|
---|
1105 | uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir, PRTFSFATDIR *ppDir)
|
---|
1106 | {
|
---|
1107 | Assert((idxCluster == UINT32_MAX) != (offDisk == UINT64_MAX));
|
---|
1108 | *ppDir = NULL;
|
---|
1109 |
|
---|
1110 | PRTFSFATDIR pNewDir;
|
---|
1111 | int rc = RTVfsNewDir(&g_rtFsFatDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
|
---|
1112 | NIL_RTVFSLOCK, phVfsDir, (void **)&pNewDir);
|
---|
1113 | if (RT_SUCCESS(rc))
|
---|
1114 | {
|
---|
1115 | /*
|
---|
1116 | * Initialize it all so rtFsFatDir_Close doesn't trip up in anyway.
|
---|
1117 | */
|
---|
1118 | RTListInit(&pNewDir->OpenChildren);
|
---|
1119 | if (pDirEntry)
|
---|
1120 | rtFsFatObj_InitFromDirEntry(&pNewDir->Core, pDirEntry, offDirEntry, pThis);
|
---|
1121 | else
|
---|
1122 | rtFsFatObj_InitDummy(&pNewDir->Core, offDirEntry, cbDir, RTFS_DOS_DIRECTORY, pThis);
|
---|
1123 |
|
---|
1124 | pNewDir->cEntries = cbDir / sizeof(FATDIRENTRY);
|
---|
1125 | pNewDir->fIsFlatRootDir = idxCluster == UINT32_MAX;
|
---|
1126 | pNewDir->fFullyBuffered = pNewDir->fIsFlatRootDir;
|
---|
1127 | if (pNewDir->fFullyBuffered)
|
---|
1128 | {
|
---|
1129 | pNewDir->u.Full.offDir = UINT64_MAX;
|
---|
1130 | pNewDir->u.Full.cSectors = 0;
|
---|
1131 | pNewDir->u.Full.cDirtySectors = 0;
|
---|
1132 | pNewDir->u.Full.paEntries = NULL;
|
---|
1133 | pNewDir->u.Full.pbDirtySectors = NULL;
|
---|
1134 | }
|
---|
1135 | else
|
---|
1136 | {
|
---|
1137 | pNewDir->u.Simple.offOnDisk = UINT64_MAX;
|
---|
1138 | pNewDir->u.Simple.offInDir = UINT32_MAX;
|
---|
1139 | pNewDir->u.Simple.u32Reserved = 0;
|
---|
1140 | pNewDir->u.Simple.paEntries = NULL;
|
---|
1141 | pNewDir->u.Simple.fDirty = false;
|
---|
1142 | }
|
---|
1143 |
|
---|
1144 | /*
|
---|
1145 | * If clustered backing, read the chain and see if we cannot still do the full buffering.
|
---|
1146 | */
|
---|
1147 | if (idxCluster != UINT32_MAX)
|
---|
1148 | {
|
---|
1149 | rc = rtFsFatClusterMap_ReadClusterChain(pThis, idxCluster, &pNewDir->Core.Clusters);
|
---|
1150 | if (RT_SUCCESS(rc))
|
---|
1151 | {
|
---|
1152 | if ( pNewDir->Core.Clusters.cClusters >= 1
|
---|
1153 | && pNewDir->Core.Clusters.cbChain <= _64K
|
---|
1154 | && rtFsFatChain_IsContiguous(&pNewDir->Core.Clusters))
|
---|
1155 | pNewDir->fFullyBuffered = true;
|
---|
1156 | }
|
---|
1157 | }
|
---|
1158 |
|
---|
1159 | if (RT_SUCCESS(rc))
|
---|
1160 | {
|
---|
1161 | RT_NOREF(pParentDir);
|
---|
1162 | }
|
---|
1163 |
|
---|
1164 | RTVfsDirRelease(*phVfsDir);
|
---|
1165 | }
|
---|
1166 | *phVfsDir = NIL_RTVFSDIR;
|
---|
1167 | *ppDir = NULL;
|
---|
1168 | return rc;
|
---|
1169 | }
|
---|
1170 |
|
---|
1171 |
|
---|
1172 |
|
---|
1173 |
|
---|
1174 |
|
---|
1175 | /**
|
---|
1176 | * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
|
---|
1177 | */
|
---|
1178 | static DECLCALLBACK(int) rtFsFatVol_Close(void *pvThis)
|
---|
1179 | {
|
---|
1180 | PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
|
---|
1181 | int rc = rtFsFatClusterMap_Destroy(pThis);
|
---|
1182 |
|
---|
1183 | if (pThis->hVfsRootDir != NIL_RTVFSDIR)
|
---|
1184 | {
|
---|
1185 | Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
|
---|
1186 | uint32_t cRefs = RTVfsDirRelease(pThis->hVfsRootDir);
|
---|
1187 | Assert(cRefs == 0);
|
---|
1188 | pThis->hVfsRootDir = NIL_RTVFSDIR;
|
---|
1189 | pThis->pRootDir = NULL;
|
---|
1190 | }
|
---|
1191 |
|
---|
1192 | RTVfsFileRelease(pThis->hVfsBacking);
|
---|
1193 | pThis->hVfsBacking = NIL_RTVFSFILE;
|
---|
1194 |
|
---|
1195 | return rc;
|
---|
1196 | }
|
---|
1197 |
|
---|
1198 |
|
---|
1199 | /**
|
---|
1200 | * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
|
---|
1201 | */
|
---|
1202 | static DECLCALLBACK(int) rtFsFatVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
|
---|
1203 | {
|
---|
1204 | RT_NOREF(pvThis, pObjInfo, enmAddAttr);
|
---|
1205 | return VERR_WRONG_TYPE;
|
---|
1206 | }
|
---|
1207 |
|
---|
1208 |
|
---|
1209 | /**
|
---|
1210 | * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
|
---|
1211 | */
|
---|
1212 | static DECLCALLBACK(int) rtFsFatVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
|
---|
1213 | {
|
---|
1214 | RT_NOREF(pvThis, phVfsDir);
|
---|
1215 | return VERR_NOT_IMPLEMENTED;
|
---|
1216 | }
|
---|
1217 |
|
---|
1218 |
|
---|
1219 | /**
|
---|
1220 | * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
|
---|
1221 | */
|
---|
1222 | static DECLCALLBACK(int) rtFsFatVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
|
---|
1223 | {
|
---|
1224 | RT_NOREF(pvThis, off, cb, pfUsed);
|
---|
1225 | return VERR_NOT_IMPLEMENTED;
|
---|
1226 | }
|
---|
1227 |
|
---|
1228 |
|
---|
1229 | DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsFatVolOps =
|
---|
1230 | {
|
---|
1231 | { /* Obj */
|
---|
1232 | RTVFSOBJOPS_VERSION,
|
---|
1233 | RTVFSOBJTYPE_VFS,
|
---|
1234 | "FatVol",
|
---|
1235 | rtFsFatVol_Close,
|
---|
1236 | rtFsFatVol_QueryInfo,
|
---|
1237 | RTVFSOBJOPS_VERSION
|
---|
1238 | },
|
---|
1239 | RTVFSOPS_VERSION,
|
---|
1240 | 0 /* fFeatures */,
|
---|
1241 | rtFsFatVol_OpenRoot,
|
---|
1242 | rtFsFatVol_IsRangeInUse,
|
---|
1243 | RTVFSOPS_VERSION
|
---|
1244 | };
|
---|
1245 |
|
---|
1246 |
|
---|
1247 | /**
|
---|
1248 | * Tries to detect a DOS 1.x formatted image and fills in the BPB fields.
|
---|
1249 | *
|
---|
1250 | * There is no BPB here, but fortunately, there isn't much variety.
|
---|
1251 | *
|
---|
1252 | * @returns IPRT status code.
|
---|
1253 | * @param pThis The FAT volume instance, BPB derived fields are filled
|
---|
1254 | * in on success.
|
---|
1255 | * @param pBootSector The boot sector.
|
---|
1256 | * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
|
---|
1257 | * the boot sector.
|
---|
1258 | * @param pErrInfo Where to return additional error information.
|
---|
1259 | */
|
---|
1260 | static int rtFsFatVolTryInitDos1x(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t const *pbFatSector,
|
---|
1261 | PRTERRINFO pErrInfo)
|
---|
1262 | {
|
---|
1263 | /*
|
---|
1264 | * PC-DOS 1.0 does a 2fh byte short jump w/o any NOP following it.
|
---|
1265 | * Instead the following are three words and a 9 byte build date
|
---|
1266 | * string. The remaining space is zero filled.
|
---|
1267 | *
|
---|
1268 | * Note! No idea how this would look like for 8" floppies, only got 5"1/4'.
|
---|
1269 | *
|
---|
1270 | * ASSUME all non-BPB disks are using this format.
|
---|
1271 | */
|
---|
1272 | if ( pBootSector->abJmp[0] != 0xeb /* jmp rel8 */
|
---|
1273 | || pBootSector->abJmp[1] < 0x2f
|
---|
1274 | || pBootSector->abJmp[1] >= 0x80
|
---|
1275 | || pBootSector->abJmp[2] == 0x90 /* nop */)
|
---|
1276 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
|
---|
1277 | "No DOS v1.0 bootsector either - invalid jmp: %.3Rhxs", pBootSector->abJmp);
|
---|
1278 | uint32_t const offJump = 2 + pBootSector->abJmp[1];
|
---|
1279 | uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
|
---|
1280 | Assert(offFirstZero >= RT_OFFSETOF(FATBOOTSECTOR, Bpb));
|
---|
1281 | uint32_t const cbZeroPad = RT_MIN(offJump - offFirstZero,
|
---|
1282 | sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_OFFSETOF(FATBOOTSECTOR, Bpb)));
|
---|
1283 |
|
---|
1284 | if (!ASMMemIsAllU8((uint8_t const *)pBootSector + offFirstZero, cbZeroPad, 0))
|
---|
1285 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
|
---|
1286 | "No DOS v1.0 bootsector either - expected zero padding %#x LB %#x: %.*Rhxs",
|
---|
1287 | offFirstZero, cbZeroPad, cbZeroPad, (uint8_t const *)pBootSector + offFirstZero);
|
---|
1288 |
|
---|
1289 | /*
|
---|
1290 | * Check the FAT ID so we can tell if this is double or single sided,
|
---|
1291 | * as well as being a valid FAT12 start.
|
---|
1292 | */
|
---|
1293 | if ( (pbFatSector[0] != 0xfe && pbFatSector[0] != 0xff)
|
---|
1294 | || pbFatSector[1] != 0xff
|
---|
1295 | || pbFatSector[2] != 0xff)
|
---|
1296 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
|
---|
1297 | "No DOS v1.0 bootsector either - unexpected start of FAT: %.3Rhxs", pbFatSector);
|
---|
1298 |
|
---|
1299 | /*
|
---|
1300 | * Fixed DOS 1.0 config.
|
---|
1301 | */
|
---|
1302 | pThis->enmFatType = RTFSFATTYPE_FAT12;
|
---|
1303 | pThis->bMedia = pbFatSector[0];
|
---|
1304 | pThis->cReservedSectors = 1;
|
---|
1305 | pThis->cbSector = 512;
|
---|
1306 | pThis->cbCluster = pThis->bMedia == 0xfe ? 1024 : 512;
|
---|
1307 | pThis->cFats = 2;
|
---|
1308 | pThis->cbFat = 512;
|
---|
1309 | pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * 512;
|
---|
1310 | pThis->aoffFats[1] = pThis->aoffFats[0] + pThis->cbFat;
|
---|
1311 | pThis->offRootDir = pThis->aoffFats[1] + pThis->cbFat;
|
---|
1312 | pThis->cRootDirEntries = 512;
|
---|
1313 | pThis->offFirstCluster = pThis->offRootDir + RT_ALIGN_32(pThis->cRootDirEntries * sizeof(FATDIRENTRY),
|
---|
1314 | pThis->cbSector);
|
---|
1315 | pThis->cbTotalSize = pThis->bMedia == 0xfe ? 8 * 1 * 40 * 512 : 8 * 2 * 40 * 512;
|
---|
1316 | pThis->cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
|
---|
1317 | return VINF_SUCCESS;
|
---|
1318 | }
|
---|
1319 |
|
---|
1320 |
|
---|
1321 | /**
|
---|
1322 | * Worker for rtFsFatVolTryInitDos2Plus that handles remaining BPB fields.
|
---|
1323 | *
|
---|
1324 | * @returns IPRT status code.
|
---|
1325 | * @param pThis The FAT volume instance, BPB derived fields are filled
|
---|
1326 | * in on success.
|
---|
1327 | * @param pBootSector The boot sector.
|
---|
1328 | * @param fMaybe331 Set if it could be a DOS v3.31 BPB.
|
---|
1329 | * @param pErrInfo Where to return additional error information.
|
---|
1330 | */
|
---|
1331 | static int rtFsFatVolTryInitDos2PlusBpb(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, bool fMaybe331, PRTERRINFO pErrInfo)
|
---|
1332 | {
|
---|
1333 | /*
|
---|
1334 | * Figure total sector count. Could both be zero, in which case we have to
|
---|
1335 | * fall back on the size of the backing stuff.
|
---|
1336 | */
|
---|
1337 | if (pBootSector->Bpb.Bpb20.cTotalSectors16 != 0)
|
---|
1338 | pThis->cbTotalSize = pBootSector->Bpb.Bpb20.cTotalSectors16 * pThis->cbSector;
|
---|
1339 | else if ( pBootSector->Bpb.Bpb331.cTotalSectors32 != 0
|
---|
1340 | && fMaybe331)
|
---|
1341 | pThis->cbTotalSize = pBootSector->Bpb.Bpb331.cTotalSectors32 * (uint64_t)pThis->cbSector;
|
---|
1342 | else
|
---|
1343 | pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
|
---|
1344 | if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
|
---|
1345 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1346 | "Bogus FAT12/16 total or reserved sector count: %#x vs %#x",
|
---|
1347 | pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
|
---|
1348 |
|
---|
1349 | /*
|
---|
1350 | * The fat size. Complete FAT offsets.
|
---|
1351 | */
|
---|
1352 | if ( pBootSector->Bpb.Bpb20.cSectorsPerFat == 0
|
---|
1353 | || ((uint32_t)pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cFats + 1) * pThis->cbSector > pThis->cbTotalSize)
|
---|
1354 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 sectors per FAT: %#x (total sectors %#RX64)",
|
---|
1355 | pBootSector->Bpb.Bpb20.cSectorsPerFat, pThis->cbTotalSize / pThis->cbSector);
|
---|
1356 | pThis->cbFat = pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cbSector;
|
---|
1357 |
|
---|
1358 | AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
|
---|
1359 | for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
|
---|
1360 | pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
|
---|
1361 |
|
---|
1362 | /*
|
---|
1363 | * Do root directory calculations.
|
---|
1364 | */
|
---|
1365 | pThis->idxRootDirCluster = UINT32_MAX;
|
---|
1366 | pThis->offRootDir = pThis->aoffFats[pThis->cFats];
|
---|
1367 | if (pThis->cRootDirEntries == 0)
|
---|
1368 | return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero FAT12/16 root directory size");
|
---|
1369 | pThis->cbRootDir = pThis->cRootDirEntries * sizeof(FATDIRENTRY);
|
---|
1370 | pThis->cbRootDir = RT_ALIGN_32(pThis->cbRootDir, pThis->cbSector);
|
---|
1371 |
|
---|
1372 | /*
|
---|
1373 | * First cluster and cluster count checks and calcs. Determin FAT type.
|
---|
1374 | */
|
---|
1375 | pThis->offFirstCluster = pThis->offRootDir + pThis->cbRootDir;
|
---|
1376 | uint64_t cbSystemStuff = pThis->offFirstCluster - pThis->offBootSector;
|
---|
1377 | if (cbSystemStuff >= pThis->cbTotalSize)
|
---|
1378 | return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 total size, root dir, or fat size");
|
---|
1379 | pThis->cClusters = (pThis->cbTotalSize - cbSystemStuff) / pThis->cbCluster;
|
---|
1380 |
|
---|
1381 | if (pThis->cClusters >= FAT_LAST_FAT16_DATA_CLUSTER)
|
---|
1382 | {
|
---|
1383 | pThis->cClusters = FAT_LAST_FAT16_DATA_CLUSTER + 1;
|
---|
1384 | pThis->enmFatType = RTFSFATTYPE_FAT16;
|
---|
1385 | }
|
---|
1386 | else if (pThis->cClusters > FAT_LAST_FAT12_DATA_CLUSTER)
|
---|
1387 | pThis->enmFatType = RTFSFATTYPE_FAT16;
|
---|
1388 | else
|
---|
1389 | pThis->enmFatType = RTFSFATTYPE_FAT12; /** @todo Not sure if this is entirely the right way to go about it... */
|
---|
1390 |
|
---|
1391 | uint32_t cClustersPerFat;
|
---|
1392 | if (pThis->enmFatType == RTFSFATTYPE_FAT16)
|
---|
1393 | cClustersPerFat = pThis->cbFat / 2;
|
---|
1394 | else
|
---|
1395 | cClustersPerFat = pThis->cbFat * 2 / 3;
|
---|
1396 | if (pThis->cClusters > cClustersPerFat)
|
---|
1397 | pThis->cClusters = cClustersPerFat;
|
---|
1398 |
|
---|
1399 | return VINF_SUCCESS;
|
---|
1400 | }
|
---|
1401 |
|
---|
1402 |
|
---|
1403 | /**
|
---|
1404 | * Worker for rtFsFatVolTryInitDos2Plus and rtFsFatVolTryInitDos2PlusFat32 that
|
---|
1405 | * handles common extended BPBs fields.
|
---|
1406 | *
|
---|
1407 | * @returns IPRT status code.
|
---|
1408 | * @param pThis The FAT volume instance.
|
---|
1409 | * @param bExtSignature The extended BPB signature.
|
---|
1410 | * @param uSerialNumber The serial number.
|
---|
1411 | * @param pachLabel Pointer to the volume label field.
|
---|
1412 | * @param pachType Pointer to the file system type field.
|
---|
1413 | */
|
---|
1414 | static void rtFsFatVolInitCommonEbpbBits(PRTFSFATVOL pThis, uint8_t bExtSignature, uint32_t uSerialNumber,
|
---|
1415 | char const *pachLabel, char const *pachType)
|
---|
1416 | {
|
---|
1417 | pThis->uSerialNo = uSerialNumber;
|
---|
1418 | if (bExtSignature == FATEBPB_SIGNATURE)
|
---|
1419 | {
|
---|
1420 | memcpy(pThis->szLabel, pachLabel, RT_SIZEOFMEMB(FATEBPB, achLabel));
|
---|
1421 | pThis->szLabel[RT_SIZEOFMEMB(FATEBPB, achLabel)] = '\0';
|
---|
1422 | RTStrStrip(pThis->szLabel);
|
---|
1423 |
|
---|
1424 | memcpy(pThis->szType, pachType, RT_SIZEOFMEMB(FATEBPB, achType));
|
---|
1425 | pThis->szType[RT_SIZEOFMEMB(FATEBPB, achType)] = '\0';
|
---|
1426 | RTStrStrip(pThis->szType);
|
---|
1427 | }
|
---|
1428 | else
|
---|
1429 | {
|
---|
1430 | pThis->szLabel[0] = '\0';
|
---|
1431 | pThis->szType[0] = '\0';
|
---|
1432 | }
|
---|
1433 | }
|
---|
1434 |
|
---|
1435 |
|
---|
1436 | /**
|
---|
1437 | * Worker for rtFsFatVolTryInitDos2Plus that deals with FAT32.
|
---|
1438 | *
|
---|
1439 | * @returns IPRT status code.
|
---|
1440 | * @param pThis The FAT volume instance, BPB derived fields are filled
|
---|
1441 | * in on success.
|
---|
1442 | * @param pBootSector The boot sector.
|
---|
1443 | * @param pErrInfo Where to return additional error information.
|
---|
1444 | */
|
---|
1445 | static int rtFsFatVolTryInitDos2PlusFat32(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, PRTERRINFO pErrInfo)
|
---|
1446 | {
|
---|
1447 | pThis->enmFatType = RTFSFATTYPE_FAT32;
|
---|
1448 | pThis->fFat32Flags = pBootSector->Bpb.Fat32Ebpb.fFlags;
|
---|
1449 |
|
---|
1450 | if (pBootSector->Bpb.Fat32Ebpb.uVersion != FAT32EBPB_VERSION_0_0)
|
---|
1451 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Unsupported FAT32 version: %d.%d (%#x)",
|
---|
1452 | RT_HI_U8(pBootSector->Bpb.Fat32Ebpb.uVersion), RT_LO_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),
|
---|
1453 | pBootSector->Bpb.Fat32Ebpb.uVersion);
|
---|
1454 |
|
---|
1455 | /*
|
---|
1456 | * Figure total sector count. We expected it to be filled in.
|
---|
1457 | */
|
---|
1458 | bool fUsing64BitTotalSectorCount = false;
|
---|
1459 | if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 != 0)
|
---|
1460 | pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 * pThis->cbSector;
|
---|
1461 | else if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 != 0)
|
---|
1462 | pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 * (uint64_t)pThis->cbSector;
|
---|
1463 | else if ( pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 <= UINT64_MAX / 512
|
---|
1464 | && pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 > 3
|
---|
1465 | && pBootSector->Bpb.Fat32Ebpb.bExtSignature != FATEBPB_SIGNATURE_OLD)
|
---|
1466 | {
|
---|
1467 | pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 * pThis->cbSector;
|
---|
1468 | fUsing64BitTotalSectorCount = true;
|
---|
1469 | }
|
---|
1470 | else
|
---|
1471 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 total sector count out of range: %#RX64",
|
---|
1472 | pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64);
|
---|
1473 | if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
|
---|
1474 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1475 | "Bogus FAT32 total or reserved sector count: %#x vs %#x",
|
---|
1476 | pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
|
---|
1477 |
|
---|
1478 | /*
|
---|
1479 | * Fat size. We check the 16-bit field even if it probably should be zero all the time.
|
---|
1480 | */
|
---|
1481 | if (pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat != 0)
|
---|
1482 | {
|
---|
1483 | if ( pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != 0
|
---|
1484 | && pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat)
|
---|
1485 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1486 | "Both 16-bit and 32-bit FAT size fields are set: %#RX16 vs %#RX32",
|
---|
1487 | pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat, pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
|
---|
1488 | pThis->cbFat = pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat * pThis->cbSector;
|
---|
1489 | }
|
---|
1490 | else
|
---|
1491 | {
|
---|
1492 | uint64_t cbFat = pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 * (uint64_t)pThis->cbSector;
|
---|
1493 | if ( cbFat == 0
|
---|
1494 | || cbFat >= (FAT_LAST_FAT32_DATA_CLUSTER + 1) * 4 + pThis->cbSector * 16)
|
---|
1495 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1496 | "Bogus 32-bit FAT size: %#RX32", pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
|
---|
1497 | pThis->cbFat = (uint32_t)cbFat;
|
---|
1498 | }
|
---|
1499 |
|
---|
1500 | /*
|
---|
1501 | * Complete the FAT offsets and first cluster offset, then calculate number
|
---|
1502 | * of data clusters.
|
---|
1503 | */
|
---|
1504 | AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
|
---|
1505 | for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
|
---|
1506 | pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
|
---|
1507 | pThis->offFirstCluster = pThis->aoffFats[pThis->cFats];
|
---|
1508 |
|
---|
1509 | if (pThis->offFirstCluster - pThis->offBootSector >= pThis->cbTotalSize)
|
---|
1510 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1511 | "Bogus 32-bit FAT size or total sector count: cFats=%d cbFat=%#x cbTotalSize=%#x",
|
---|
1512 | pThis->cFats, pThis->cbFat, pThis->cbTotalSize);
|
---|
1513 |
|
---|
1514 | uint64_t cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
|
---|
1515 | if (cClusters <= FAT_LAST_FAT32_DATA_CLUSTER)
|
---|
1516 | pThis->cClusters = (uint32_t)cClusters;
|
---|
1517 | else
|
---|
1518 | pThis->cClusters = FAT_LAST_FAT32_DATA_CLUSTER + 1;
|
---|
1519 | if (pThis->cClusters > pThis->cbFat / 4)
|
---|
1520 | pThis->cClusters = pThis->cbFat / 4;
|
---|
1521 |
|
---|
1522 | /*
|
---|
1523 | * Root dir cluster.
|
---|
1524 | */
|
---|
1525 | if ( pBootSector->Bpb.Fat32Ebpb.uRootDirCluster < FAT_FIRST_DATA_CLUSTER
|
---|
1526 | || pBootSector->Bpb.Fat32Ebpb.uRootDirCluster >= pThis->cClusters)
|
---|
1527 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1528 | "Bogus FAT32 root directory cluster: %#x", pBootSector->Bpb.Fat32Ebpb.uRootDirCluster);
|
---|
1529 | pThis->idxRootDirCluster = pBootSector->Bpb.Fat32Ebpb.uRootDirCluster;
|
---|
1530 | pThis->offRootDir = pThis->offFirstCluster
|
---|
1531 | + (pBootSector->Bpb.Fat32Ebpb.uRootDirCluster - FAT_FIRST_DATA_CLUSTER) * pThis->cbCluster;
|
---|
1532 |
|
---|
1533 | /*
|
---|
1534 | * Info sector.
|
---|
1535 | */
|
---|
1536 | if ( pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == 0
|
---|
1537 | || pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == UINT16_MAX)
|
---|
1538 | pThis->offFat32InfoSector = UINT64_MAX;
|
---|
1539 | else if (pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo >= pThis->cReservedSectors)
|
---|
1540 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1541 | "Bogus FAT32 info sector number: %#x (reserved sectors %#x)",
|
---|
1542 | pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pThis->cReservedSectors);
|
---|
1543 | else
|
---|
1544 | {
|
---|
1545 | pThis->offFat32InfoSector = pThis->cbSector * pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo + pThis->offBootSector;
|
---|
1546 | int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->offFat32InfoSector,
|
---|
1547 | &pThis->Fat32InfoSector, sizeof(pThis->Fat32InfoSector), NULL);
|
---|
1548 | if (RT_FAILURE(rc))
|
---|
1549 | return RTErrInfoSetF(pErrInfo, rc, "Failed to read FAT32 info sector at offset %#RX64", pThis->offFat32InfoSector);
|
---|
1550 | if ( pThis->Fat32InfoSector.uSignature1 != FAT32INFOSECTOR_SIGNATURE_1
|
---|
1551 | || pThis->Fat32InfoSector.uSignature2 != FAT32INFOSECTOR_SIGNATURE_2
|
---|
1552 | || pThis->Fat32InfoSector.uSignature3 != FAT32INFOSECTOR_SIGNATURE_3)
|
---|
1553 | return RTErrInfoSetF(pErrInfo, rc, "FAT32 info sector signature mismatch: %#x %#x %#x",
|
---|
1554 | pThis->Fat32InfoSector.uSignature1, pThis->Fat32InfoSector.uSignature2,
|
---|
1555 | pThis->Fat32InfoSector.uSignature3);
|
---|
1556 | }
|
---|
1557 |
|
---|
1558 | /*
|
---|
1559 | * Boot sector copy.
|
---|
1560 | */
|
---|
1561 | if ( pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == 0
|
---|
1562 | || pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == UINT16_MAX)
|
---|
1563 | {
|
---|
1564 | pThis->cBootSectorCopies = 0;
|
---|
1565 | pThis->offBootSectorCopies = UINT64_MAX;
|
---|
1566 | }
|
---|
1567 | else if (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo >= pThis->cReservedSectors)
|
---|
1568 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1569 | "Bogus FAT32 info boot sector copy location: %#x (reserved sectors %#x)",
|
---|
1570 | pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo, pThis->cReservedSectors);
|
---|
1571 | else
|
---|
1572 | {
|
---|
1573 | /** @todo not sure if cbSector is correct here. */
|
---|
1574 | pThis->cBootSectorCopies = 3;
|
---|
1575 | if ( (uint32_t)pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo + pThis->cBootSectorCopies
|
---|
1576 | > pThis->cReservedSectors)
|
---|
1577 | pThis->cBootSectorCopies = (uint8_t)(pThis->cReservedSectors - pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
|
---|
1578 | pThis->offBootSectorCopies = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo * pThis->cbSector + pThis->offBootSector;
|
---|
1579 | if ( pThis->offFat32InfoSector != UINT64_MAX
|
---|
1580 | && pThis->offFat32InfoSector - pThis->offBootSectorCopies < (uint64_t)(pThis->cBootSectorCopies * pThis->cbSector))
|
---|
1581 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 info sector and boot sector copies overlap: %#x vs %#x",
|
---|
1582 | pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
|
---|
1583 | }
|
---|
1584 |
|
---|
1585 | /*
|
---|
1586 | * Serial number, label and type.
|
---|
1587 | */
|
---|
1588 | rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Fat32Ebpb.bExtSignature, pBootSector->Bpb.Fat32Ebpb.uSerialNumber,
|
---|
1589 | pBootSector->Bpb.Fat32Ebpb.achLabel,
|
---|
1590 | fUsing64BitTotalSectorCount ? pBootSector->achOemName : pBootSector->Bpb.Fat32Ebpb.achLabel);
|
---|
1591 | if (pThis->szType[0] == '\0')
|
---|
1592 | memcpy(pThis->szType, "FAT32", 6);
|
---|
1593 |
|
---|
1594 | return VINF_SUCCESS;
|
---|
1595 | }
|
---|
1596 |
|
---|
1597 |
|
---|
1598 | /**
|
---|
1599 | * Tries to detect a DOS 2.0+ formatted image and fills in the BPB fields.
|
---|
1600 | *
|
---|
1601 | * We ASSUME BPB here, but need to figure out which version of the BPB it is,
|
---|
1602 | * which is lots of fun.
|
---|
1603 | *
|
---|
1604 | * @returns IPRT status code.
|
---|
1605 | * @param pThis The FAT volume instance, BPB derived fields are filled
|
---|
1606 | * in on success.
|
---|
1607 | * @param pBootSector The boot sector.
|
---|
1608 | * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
|
---|
1609 | * the boot sector. On successful return it will contain
|
---|
1610 | * the first FAT sector.
|
---|
1611 | * @param pErrInfo Where to return additional error information.
|
---|
1612 | */
|
---|
1613 | static int rtFsFatVolTryInitDos2Plus(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t *pbFatSector, PRTERRINFO pErrInfo)
|
---|
1614 | {
|
---|
1615 | /*
|
---|
1616 | * Check if we've got a known jump instruction first, because that will
|
---|
1617 | * give us a max (E)BPB size hint.
|
---|
1618 | */
|
---|
1619 | uint8_t offJmp = UINT8_MAX;
|
---|
1620 | if ( pBootSector->abJmp[0] == 0xeb
|
---|
1621 | && pBootSector->abJmp[1] <= 0x7f)
|
---|
1622 | offJmp = pBootSector->abJmp[1] + 2;
|
---|
1623 | else if ( pBootSector->abJmp[0] == 0x90
|
---|
1624 | && pBootSector->abJmp[1] == 0xeb
|
---|
1625 | && pBootSector->abJmp[2] <= 0x7f)
|
---|
1626 | offJmp = pBootSector->abJmp[2] + 3;
|
---|
1627 | else if ( pBootSector->abJmp[0] == 0xe9
|
---|
1628 | && pBootSector->abJmp[2] <= 0x7f)
|
---|
1629 | offJmp = RT_MIN(127, RT_MAKE_U16(pBootSector->abJmp[1], pBootSector->abJmp[2]));
|
---|
1630 | uint8_t const cbMaxBpb = offJmp - RT_OFFSETOF(FATBOOTSECTOR, Bpb);
|
---|
1631 |
|
---|
1632 | /*
|
---|
1633 | * Do the basic DOS v2.0 BPB fields.
|
---|
1634 | */
|
---|
1635 | if (cbMaxBpb < sizeof(FATBPB20))
|
---|
1636 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
|
---|
1637 | "DOS signature, but jmp too short for any BPB: %#x (max %#x BPB)", offJmp, cbMaxBpb);
|
---|
1638 |
|
---|
1639 | if (pBootSector->Bpb.Bpb20.cFats == 0)
|
---|
1640 | return RTErrInfoSet(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, number of FATs is zero, so not FAT file system");
|
---|
1641 | if (pBootSector->Bpb.Bpb20.cFats > 4)
|
---|
1642 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many FATs: %#x", pBootSector->Bpb.Bpb20.cFats);
|
---|
1643 | pThis->cFats = pBootSector->Bpb.Bpb20.cFats;
|
---|
1644 |
|
---|
1645 | if (!FATBPB_MEDIA_IS_VALID(pBootSector->Bpb.Bpb20.bMedia))
|
---|
1646 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
|
---|
1647 | "DOS signature, invalid media byte: %#x", pBootSector->Bpb.Bpb20.bMedia);
|
---|
1648 | pThis->bMedia = pBootSector->Bpb.Bpb20.bMedia;
|
---|
1649 |
|
---|
1650 | if (!RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cbSector))
|
---|
1651 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
|
---|
1652 | "DOS signature, sector size not power of two: %#x", pBootSector->Bpb.Bpb20.cbSector);
|
---|
1653 | if ( pBootSector->Bpb.Bpb20.cbSector != 512
|
---|
1654 | && pBootSector->Bpb.Bpb20.cbSector != 4096
|
---|
1655 | && pBootSector->Bpb.Bpb20.cbSector != 1024
|
---|
1656 | && pBootSector->Bpb.Bpb20.cbSector != 128)
|
---|
1657 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
|
---|
1658 | "DOS signature, unsupported sector size: %#x", pBootSector->Bpb.Bpb20.cbSector);
|
---|
1659 | pThis->cbSector = pBootSector->Bpb.Bpb20.cbSector;
|
---|
1660 |
|
---|
1661 | if ( !RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cSectorsPerCluster)
|
---|
1662 | || !pBootSector->Bpb.Bpb20.cSectorsPerCluster)
|
---|
1663 | return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, cluster size not non-zero power of two: %#x",
|
---|
1664 | pBootSector->Bpb.Bpb20.cSectorsPerCluster);
|
---|
1665 | pThis->cbCluster = pBootSector->Bpb.Bpb20.cSectorsPerCluster * pThis->cbSector;
|
---|
1666 |
|
---|
1667 | uint64_t const cMaxRoot = (pThis->cbBacking - pThis->offBootSector - 512) / sizeof(FATDIRENTRY); /* we'll check again later. */
|
---|
1668 | if (pBootSector->Bpb.Bpb20.cMaxRootDirEntries >= cMaxRoot)
|
---|
1669 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many root entries: %#x (max %#RX64)",
|
---|
1670 | pBootSector->Bpb.Bpb20.cSectorsPerCluster, cMaxRoot);
|
---|
1671 | pThis->cRootDirEntries = pBootSector->Bpb.Bpb20.cMaxRootDirEntries;
|
---|
1672 |
|
---|
1673 | if ( pBootSector->Bpb.Bpb20.cReservedSectors == 0
|
---|
1674 | || pBootSector->Bpb.Bpb20.cReservedSectors >= _32K)
|
---|
1675 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1676 | "DOS signature, bogus reserved sector count: %#x", pBootSector->Bpb.Bpb20.cReservedSectors);
|
---|
1677 | pThis->cReservedSectors = pBootSector->Bpb.Bpb20.cReservedSectors;
|
---|
1678 | pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * pThis->cbSector;
|
---|
1679 |
|
---|
1680 | /*
|
---|
1681 | * Jump ahead and check for FAT32 EBPB.
|
---|
1682 | * If found, we simply ASSUME it's a FAT32 file system.
|
---|
1683 | */
|
---|
1684 | int rc;
|
---|
1685 | if ( ( sizeof(FAT32EBPB) <= cbMaxBpb
|
---|
1686 | && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE)
|
---|
1687 | || ( RT_OFFSETOF(FAT32EBPB, achLabel) <= cbMaxBpb
|
---|
1688 | && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
|
---|
1689 | {
|
---|
1690 | rc = rtFsFatVolTryInitDos2PlusFat32(pThis, pBootSector, pErrInfo);
|
---|
1691 | if (RT_FAILURE(rc))
|
---|
1692 | return rc;
|
---|
1693 | }
|
---|
1694 | else
|
---|
1695 | {
|
---|
1696 | /*
|
---|
1697 | * Check for extended BPB, otherwise we'll have to make qualified guesses
|
---|
1698 | * about what kind of BPB we're up against based on jmp offset and zero fields.
|
---|
1699 | * ASSUMES either FAT16 or FAT12.
|
---|
1700 | */
|
---|
1701 | if ( ( sizeof(FATEBPB) <= cbMaxBpb
|
---|
1702 | && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE)
|
---|
1703 | || ( RT_OFFSETOF(FATEBPB, achLabel) <= cbMaxBpb
|
---|
1704 | && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
|
---|
1705 | {
|
---|
1706 | rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Ebpb.bExtSignature, pBootSector->Bpb.Ebpb.uSerialNumber,
|
---|
1707 | pBootSector->Bpb.Ebpb.achLabel, pBootSector->Bpb.Ebpb.achType);
|
---|
1708 | rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, true /*fMaybe331*/, pErrInfo);
|
---|
1709 | }
|
---|
1710 | else
|
---|
1711 | rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, cbMaxBpb >= sizeof(FATBPB331), pErrInfo);
|
---|
1712 | if (RT_FAILURE(rc))
|
---|
1713 | return rc;
|
---|
1714 | if (pThis->szType[0] == '\0')
|
---|
1715 | memcpy(pThis->szType, pThis->enmFatType == RTFSFATTYPE_FAT12 ? "FAT12" : "FAT16", 6);
|
---|
1716 | }
|
---|
1717 |
|
---|
1718 | /*
|
---|
1719 | * Check the FAT ID. May have to read a bit of the FAT into the buffer.
|
---|
1720 | */
|
---|
1721 | if (pThis->aoffFats[0] != pThis->offBootSector + 512)
|
---|
1722 | {
|
---|
1723 | rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0], pbFatSector, 512, NULL);
|
---|
1724 | if (RT_FAILURE(rc))
|
---|
1725 | return RTErrInfoSet(pErrInfo, rc, "error reading first FAT sector");
|
---|
1726 | }
|
---|
1727 | if (pbFatSector[0] != pThis->bMedia)
|
---|
1728 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
|
---|
1729 | "Media byte and FAT ID mismatch: %#x vs %#x (%.7Rhxs)", pbFatSector[0], pThis->bMedia, pbFatSector);
|
---|
1730 | switch (pThis->enmFatType)
|
---|
1731 | {
|
---|
1732 | case RTFSFATTYPE_FAT12:
|
---|
1733 | if ((pbFatSector[1] & 0xf) != 0xf)
|
---|
1734 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT12): %.3Rhxs", pbFatSector);
|
---|
1735 | pThis->idxMaxLastCluster = FAT_LAST_FAT12_DATA_CLUSTER;
|
---|
1736 | pThis->idxEndOfChain = (pbFatSector[1] >> 4) | ((uint32_t)pbFatSector[2] << 4);
|
---|
1737 | break;
|
---|
1738 |
|
---|
1739 | case RTFSFATTYPE_FAT16:
|
---|
1740 | if (pbFatSector[1] != 0xff)
|
---|
1741 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT16): %.4Rhxs", pbFatSector);
|
---|
1742 | pThis->idxMaxLastCluster = FAT_LAST_FAT16_DATA_CLUSTER;
|
---|
1743 | pThis->idxEndOfChain = RT_MAKE_U16(pbFatSector[2], pbFatSector[3]);
|
---|
1744 | break;
|
---|
1745 |
|
---|
1746 | case RTFSFATTYPE_FAT32:
|
---|
1747 | if ( pbFatSector[1] != 0xff
|
---|
1748 | || pbFatSector[2] != 0xff
|
---|
1749 | || (pbFatSector[3] & 0x0f) != 0x0f)
|
---|
1750 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT32): %.8Rhxs", pbFatSector);
|
---|
1751 | pThis->idxMaxLastCluster = FAT_LAST_FAT32_DATA_CLUSTER;
|
---|
1752 | pThis->idxEndOfChain = RT_MAKE_U32_FROM_U8(pbFatSector[4], pbFatSector[5], pbFatSector[6], pbFatSector[7]);
|
---|
1753 | break;
|
---|
1754 |
|
---|
1755 | default: AssertFailedReturn(VERR_INTERNAL_ERROR_2);
|
---|
1756 | }
|
---|
1757 | if (pThis->idxEndOfChain <= pThis->idxMaxLastCluster)
|
---|
1758 | return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus formatter end-of-chain value: %#x, must be above %#x",
|
---|
1759 | pThis->idxEndOfChain, pThis->idxMaxLastCluster);
|
---|
1760 |
|
---|
1761 | RT_NOREF(pbFatSector);
|
---|
1762 | return VINF_SUCCESS;
|
---|
1763 | }
|
---|
1764 |
|
---|
1765 |
|
---|
1766 | /**
|
---|
1767 | * Worker for RTFsFatVolOpen.
|
---|
1768 | *
|
---|
1769 | * @returns IPRT status code.
|
---|
1770 | * @param pThis The FAT VFS instance to initialize.
|
---|
1771 | * @param hVfsSelf The FAT VFS handle (no reference consumed).
|
---|
1772 | * @param hVfsBacking The file backing the alleged FAT file system.
|
---|
1773 | * Reference is consumed (via rtFsFatVol_Destroy).
|
---|
1774 | * @param fReadOnly Readonly or readwrite mount.
|
---|
1775 | * @param offBootSector The boot sector offset in bytes.
|
---|
1776 | * @param pErrInfo Where to return additional error info. Can be NULL.
|
---|
1777 | */
|
---|
1778 | static int rtFsFatVolTryInit(PRTFSFATVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking,
|
---|
1779 | bool fReadOnly, uint64_t offBootSector, PRTERRINFO pErrInfo)
|
---|
1780 | {
|
---|
1781 | /*
|
---|
1782 | * First initialize the state so that rtFsFatVol_Destroy won't trip up.
|
---|
1783 | */
|
---|
1784 | pThis->hVfsSelf = hVfsSelf;
|
---|
1785 | pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsFatVol_Destroy releases it. */
|
---|
1786 | pThis->cbBacking = 0;
|
---|
1787 | pThis->offBootSector = offBootSector;
|
---|
1788 | pThis->fReadOnly = fReadOnly;
|
---|
1789 | pThis->cReservedSectors = 1;
|
---|
1790 |
|
---|
1791 | pThis->cbSector = 512;
|
---|
1792 | pThis->cbCluster = 512;
|
---|
1793 | pThis->cClusters = 0;
|
---|
1794 | pThis->offFirstCluster = 0;
|
---|
1795 | pThis->cbTotalSize = 0;
|
---|
1796 |
|
---|
1797 | pThis->enmFatType = RTFSFATTYPE_INVALID;
|
---|
1798 | pThis->cFatEntries = 0;
|
---|
1799 | pThis->cFats = 0;
|
---|
1800 | pThis->cbFat = 0;
|
---|
1801 | for (unsigned i = 0; i < RT_ELEMENTS(pThis->aoffFats); i++)
|
---|
1802 | pThis->aoffFats[i] = UINT64_MAX;
|
---|
1803 | pThis->pFatCache = NULL;
|
---|
1804 |
|
---|
1805 | pThis->offRootDir = UINT64_MAX;
|
---|
1806 | pThis->idxRootDirCluster = UINT32_MAX;
|
---|
1807 | pThis->cRootDirEntries = UINT32_MAX;
|
---|
1808 | pThis->cbRootDir = 0;
|
---|
1809 | pThis->hVfsRootDir = NIL_RTVFSDIR;
|
---|
1810 | pThis->pRootDir = NULL;
|
---|
1811 |
|
---|
1812 | pThis->uSerialNo = 0;
|
---|
1813 | pThis->szLabel[0] = '\0';
|
---|
1814 | pThis->szType[0] = '\0';
|
---|
1815 | pThis->cBootSectorCopies = 0;
|
---|
1816 | pThis->fFat32Flags = 0;
|
---|
1817 | pThis->offBootSectorCopies = UINT64_MAX;
|
---|
1818 | pThis->offFat32InfoSector = UINT64_MAX;
|
---|
1819 | RT_ZERO(pThis->Fat32InfoSector);
|
---|
1820 |
|
---|
1821 | /*
|
---|
1822 | * Get stuff that may fail.
|
---|
1823 | */
|
---|
1824 | int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
|
---|
1825 | if (RT_FAILURE(rc))
|
---|
1826 | return rc;
|
---|
1827 | pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
|
---|
1828 |
|
---|
1829 | /*
|
---|
1830 | * Read the boot sector and the following sector (start of the allocation
|
---|
1831 | * table unless it a FAT32 FS). We'll then validate the boot sector and
|
---|
1832 | * start of the FAT, expanding the BPB into the instance data.
|
---|
1833 | */
|
---|
1834 | union
|
---|
1835 | {
|
---|
1836 | uint8_t ab[512*2];
|
---|
1837 | uint16_t au16[512*2 / 2];
|
---|
1838 | uint32_t au32[512*2 / 4];
|
---|
1839 | FATBOOTSECTOR BootSector;
|
---|
1840 | FAT32INFOSECTOR InfoSector;
|
---|
1841 | } Buf;
|
---|
1842 | RT_ZERO(Buf);
|
---|
1843 |
|
---|
1844 | rc = RTVfsFileReadAt(hVfsBacking, offBootSector, &Buf.BootSector, 512 * 2, NULL);
|
---|
1845 | if (RT_FAILURE(rc))
|
---|
1846 | return RTErrInfoSet(pErrInfo, rc, "Unable to read bootsect");
|
---|
1847 |
|
---|
1848 | /*
|
---|
1849 | * Extract info from the BPB and validate the two special FAT entries.
|
---|
1850 | *
|
---|
1851 | * Check the DOS signature first. The PC-DOS 1.0 boot floppy does not have
|
---|
1852 | * a signature and we ASSUME this is the case for all flopies formated by it.
|
---|
1853 | */
|
---|
1854 | if (Buf.BootSector.uSignature != FATBOOTSECTOR_SIGNATURE)
|
---|
1855 | {
|
---|
1856 | if (Buf.BootSector.uSignature != 0)
|
---|
1857 | return RTErrInfoSetF(pErrInfo, rc, "No DOS bootsector signature: %#06x", Buf.BootSector.uSignature);
|
---|
1858 | rc = rtFsFatVolTryInitDos1x(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
|
---|
1859 | }
|
---|
1860 | else
|
---|
1861 | rc = rtFsFatVolTryInitDos2Plus(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
|
---|
1862 | if (RT_FAILURE(rc))
|
---|
1863 | return rc;
|
---|
1864 |
|
---|
1865 | /*
|
---|
1866 | * Setup the FAT cache.
|
---|
1867 | */
|
---|
1868 | rc = rtFsFatClusterMap_Create(pThis, &Buf.ab[512], pErrInfo);
|
---|
1869 | if (RT_FAILURE(rc))
|
---|
1870 | return rc;
|
---|
1871 |
|
---|
1872 | /*
|
---|
1873 | * Create the root directory fun.
|
---|
1874 | */
|
---|
1875 | if (pThis->idxRootDirCluster == UINT32_MAX)
|
---|
1876 | rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, 0 /*offDirEntry*/,
|
---|
1877 | UINT32_MAX, pThis->offRootDir, pThis->cbRootDir,
|
---|
1878 | &pThis->hVfsRootDir, &pThis->pRootDir);
|
---|
1879 | else
|
---|
1880 | rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, 0 /*offDirEntry*/,
|
---|
1881 | pThis->idxRootDirCluster, UINT64_MAX, pThis->cbRootDir,
|
---|
1882 | &pThis->hVfsRootDir, &pThis->pRootDir);
|
---|
1883 | if (RT_FAILURE(rc))
|
---|
1884 | return rc;
|
---|
1885 |
|
---|
1886 |
|
---|
1887 | return RTErrInfoSetF(pErrInfo, VERR_NOT_IMPLEMENTED,
|
---|
1888 | "cbSector=%#x cbCluster=%#x cReservedSectors=%#x\n"
|
---|
1889 | "cFats=%#x cbFat=%#x offFirstFat=%#RX64 offSecondFat=%#RX64\n"
|
---|
1890 | "cbRootDir=%#x offRootDir=%#RX64 offFirstCluster=%#RX64",
|
---|
1891 | pThis->cbSector, pThis->cbCluster, pThis->cReservedSectors,
|
---|
1892 | pThis->cFats, pThis->cbFat, pThis->aoffFats[0], pThis->aoffFats[1],
|
---|
1893 | pThis->cbRootDir, pThis->offRootDir, pThis->offFirstCluster);
|
---|
1894 | }
|
---|
1895 |
|
---|
1896 |
|
---|
1897 | RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, uint64_t offBootSector, PRTVFS phVfs, PRTERRINFO pErrInfo)
|
---|
1898 | {
|
---|
1899 | /*
|
---|
1900 | * Quick input validation.
|
---|
1901 | */
|
---|
1902 | AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
|
---|
1903 | *phVfs = NIL_RTVFS;
|
---|
1904 |
|
---|
1905 | uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
|
---|
1906 | AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
|
---|
1907 |
|
---|
1908 | /*
|
---|
1909 | * Create a new FAT VFS instance and try initialize it using the given input file.
|
---|
1910 | */
|
---|
1911 | RTVFS hVfs = NIL_RTVFS;
|
---|
1912 | void *pvThis = NULL;
|
---|
1913 | int rc = RTVfsNew(&g_rtFsFatVolOps, sizeof(RTFSFATVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
|
---|
1914 | if (RT_SUCCESS(rc))
|
---|
1915 | {
|
---|
1916 | rc = rtFsFatVolTryInit((PRTFSFATVOL)pvThis, hVfs, hVfsFileIn, fReadOnly, offBootSector, pErrInfo);
|
---|
1917 | if (RT_SUCCESS(rc))
|
---|
1918 | *phVfs = hVfs;
|
---|
1919 | else
|
---|
1920 | RTVfsRelease(hVfs);
|
---|
1921 | }
|
---|
1922 | else
|
---|
1923 | RTVfsFileRelease(hVfsFileIn);
|
---|
1924 | return rc;
|
---|
1925 | }
|
---|
1926 |
|
---|
1927 |
|
---|
1928 | /**
|
---|
1929 | * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
|
---|
1930 | */
|
---|
1931 | static DECLCALLBACK(int) rtVfsChainFatVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
|
---|
1932 | PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
|
---|
1933 | {
|
---|
1934 | RT_NOREF(pProviderReg);
|
---|
1935 |
|
---|
1936 | /*
|
---|
1937 | * Basic checks.
|
---|
1938 | */
|
---|
1939 | if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
|
---|
1940 | return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
|
---|
1941 | if ( pElement->enmType != RTVFSOBJTYPE_VFS
|
---|
1942 | && pElement->enmType != RTVFSOBJTYPE_DIR)
|
---|
1943 | return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
|
---|
1944 | if (pElement->cArgs > 1)
|
---|
1945 | return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
|
---|
1946 |
|
---|
1947 | /*
|
---|
1948 | * Parse the flag if present, save in pElement->uProvider.
|
---|
1949 | */
|
---|
1950 | bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
|
---|
1951 | if (pElement->cArgs > 0)
|
---|
1952 | {
|
---|
1953 | const char *psz = pElement->paArgs[0].psz;
|
---|
1954 | if (*psz)
|
---|
1955 | {
|
---|
1956 | if (!strcmp(psz, "ro"))
|
---|
1957 | fReadOnly = true;
|
---|
1958 | else if (!strcmp(psz, "rw"))
|
---|
1959 | fReadOnly = false;
|
---|
1960 | else
|
---|
1961 | {
|
---|
1962 | *poffError = pElement->paArgs[0].offSpec;
|
---|
1963 | return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
|
---|
1964 | }
|
---|
1965 | }
|
---|
1966 | }
|
---|
1967 |
|
---|
1968 | pElement->uProvider = fReadOnly;
|
---|
1969 | return VINF_SUCCESS;
|
---|
1970 | }
|
---|
1971 |
|
---|
1972 |
|
---|
1973 | /**
|
---|
1974 | * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
|
---|
1975 | */
|
---|
1976 | static DECLCALLBACK(int) rtVfsChainFatVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
|
---|
1977 | PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
|
---|
1978 | PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
|
---|
1979 | {
|
---|
1980 | RT_NOREF(pProviderReg, pSpec, poffError);
|
---|
1981 |
|
---|
1982 | int rc;
|
---|
1983 | RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
|
---|
1984 | if (hVfsFileIn != NIL_RTVFSFILE)
|
---|
1985 | {
|
---|
1986 | RTVFS hVfs;
|
---|
1987 | rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, 0, &hVfs, pErrInfo);
|
---|
1988 | RTVfsFileRelease(hVfsFileIn);
|
---|
1989 | if (RT_SUCCESS(rc))
|
---|
1990 | {
|
---|
1991 | *phVfsObj = RTVfsObjFromVfs(hVfs);
|
---|
1992 | RTVfsRelease(hVfs);
|
---|
1993 | if (*phVfsObj != NIL_RTVFSOBJ)
|
---|
1994 | return VINF_SUCCESS;
|
---|
1995 | rc = VERR_VFS_CHAIN_CAST_FAILED;
|
---|
1996 | }
|
---|
1997 | }
|
---|
1998 | else
|
---|
1999 | rc = VERR_VFS_CHAIN_CAST_FAILED;
|
---|
2000 | return rc;
|
---|
2001 | }
|
---|
2002 |
|
---|
2003 |
|
---|
2004 | /**
|
---|
2005 | * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
|
---|
2006 | */
|
---|
2007 | static DECLCALLBACK(bool) rtVfsChainFatVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
|
---|
2008 | PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
|
---|
2009 | PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
|
---|
2010 | {
|
---|
2011 | RT_NOREF(pProviderReg, pSpec, pReuseSpec);
|
---|
2012 | if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
|
---|
2013 | || !pReuseElement->paArgs[0].uProvider)
|
---|
2014 | return true;
|
---|
2015 | return false;
|
---|
2016 | }
|
---|
2017 |
|
---|
2018 |
|
---|
2019 | /** VFS chain element 'file'. */
|
---|
2020 | static RTVFSCHAINELEMENTREG g_rtVfsChainFatVolReg =
|
---|
2021 | {
|
---|
2022 | /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
|
---|
2023 | /* fReserved = */ 0,
|
---|
2024 | /* pszName = */ "fat",
|
---|
2025 | /* ListEntry = */ { NULL, NULL },
|
---|
2026 | /* pszHelp = */ "Open a FAT file system, requires a file object on the left side.\n"
|
---|
2027 | "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
|
---|
2028 | /* pfnValidate = */ rtVfsChainFatVol_Validate,
|
---|
2029 | /* pfnInstantiate = */ rtVfsChainFatVol_Instantiate,
|
---|
2030 | /* pfnCanReuseElement = */ rtVfsChainFatVol_CanReuseElement,
|
---|
2031 | /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
|
---|
2032 | };
|
---|
2033 |
|
---|
2034 | RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainFatVolReg, rtVfsChainFatVolReg);
|
---|
2035 |
|
---|