VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp@ 66699

Last change on this file since 66699 was 66699, checked in by vboxsync, 8 years ago

fatvfs: assertion msg fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 178.5 KB
Line 
1/* $Id: fatvfs.cpp 66699 2017-04-27 15:50:06Z 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.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#include "internal/iprt.h"
32#include <iprt/fsvfs.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/rand.h>
42#include <iprt/string.h>
43#include <iprt/sg.h>
44#include <iprt/thread.h>
45#include <iprt/uni.h>
46#include <iprt/vfs.h>
47#include <iprt/vfslowlevel.h>
48#include <iprt/zero.h>
49#include <iprt/formats/fat.h>
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55/**
56 * Gets the cluster from a directory entry.
57 *
58 * @param a_pDirEntry Pointer to the directory entry.
59 * @param a_pVol Pointer to the volume.
60 */
61#define RTFSFAT_GET_CLUSTER(a_pDirEntry, a_pVol) \
62 ( (a_pVol)->enmFatType >= RTFSFATTYPE_FAT32 \
63 ? RT_MAKE_U32((a_pDirEntry)->idxCluster, (a_pDirEntry)->u.idxClusterHigh) \
64 : (a_pDirEntry)->idxCluster )
65
66/**
67 * Rotates a unsigned 8-bit value one bit to the right.
68 *
69 * @returns Rotated 8-bit value.
70 * @param a_bValue The value to rotate.
71 */
72#define RTFSFAT_ROT_R1_U8(a_bValue) (((a_bValue) >> 1) | (uint8_t)((a_bValue) << 7))
73
74
75/*********************************************************************************************************************************
76* Structures and Typedefs *
77*********************************************************************************************************************************/
78/** Pointer to a FAT directory instance. */
79typedef struct RTFSFATDIR *PRTFSFATDIR;
80
81
82/** The number of entire in a chain part. */
83#define RTFSFATCHAINPART_ENTRIES (256U - 4U)
84
85/**
86 * A part of the cluster chain covering up to 252 clusters.
87 */
88typedef struct RTFSFATCHAINPART
89{
90 /** List entry. */
91 RTLISTNODE ListEntry;
92 /** Chain entries. */
93 uint32_t aEntries[RTFSFATCHAINPART_ENTRIES];
94} RTFSFATCHAINPART;
95AssertCompile(sizeof(RTFSFATCHAINPART) <= _1K);
96typedef RTFSFATCHAINPART *PRTFSFATCHAINPART;
97typedef RTFSFATCHAINPART const *PCRTFSFATCHAINPART;
98
99
100/**
101 * A FAT cluster chain.
102 */
103typedef struct RTFSFATCHAIN
104{
105 /** The chain size in bytes. */
106 uint32_t cbChain;
107 /** The chain size in entries. */
108 uint32_t cClusters;
109 /** The cluster size. */
110 uint32_t cbCluster;
111 /** The shift count for converting between clusters and bytes. */
112 uint8_t cClusterByteShift;
113 /** List of chain parts (RTFSFATCHAINPART). */
114 RTLISTANCHOR ListParts;
115} RTFSFATCHAIN;
116typedef RTFSFATCHAIN *PRTFSFATCHAIN;
117typedef RTFSFATCHAIN const *PCRTFSFATCHAIN;
118
119
120/**
121 * FAT file system object (common part to files and dirs).
122 */
123typedef struct RTFSFATOBJ
124{
125 /** The parent directory keeps a list of open objects (RTFSFATOBJ). */
126 RTLISTNODE Entry;
127 /** The parent directory (not released till all children are close). */
128 PRTFSFATDIR pParentDir;
129 /** The byte offset of the directory entry in the parent dir.
130 * This is set to UINT32_MAX for the root directory. */
131 uint32_t offEntryInDir;
132 /** Attributes. */
133 RTFMODE fAttrib;
134 /** The object size. */
135 uint32_t cbObject;
136 /** The access time. */
137 RTTIMESPEC AccessTime;
138 /** The modificaton time. */
139 RTTIMESPEC ModificationTime;
140 /** The birth time. */
141 RTTIMESPEC BirthTime;
142 /** Cluster chain. */
143 RTFSFATCHAIN Clusters;
144 /** Pointer to the volume. */
145 struct RTFSFATVOL *pVol;
146 /** Set if we've maybe dirtied the FAT. */
147 bool fMaybeDirtyFat;
148 /** Set if we've maybe dirtied the directory entry. */
149 bool fMaybeDirtyDirEnt;
150} RTFSFATOBJ;
151typedef RTFSFATOBJ *PRTFSFATOBJ;
152
153typedef struct RTFSFATFILE
154{
155 /** Core FAT object info. */
156 RTFSFATOBJ Core;
157 /** The current file offset. */
158 uint32_t offFile;
159} RTFSFATFILE;
160typedef RTFSFATFILE *PRTFSFATFILE;
161
162
163/**
164 * FAT directory.
165 *
166 * We work directories in one of two buffering modes. If there are few entries
167 * or if it's the FAT12/16 root directory, we map the whole thing into memory.
168 * If it's too large, we use an inefficient sector buffer for now.
169 *
170 * Directory entry updates happens exclusively via the directory, so any open
171 * files or subdirs have a parent reference for doing that. The parent OTOH,
172 * keeps a list of open children.
173 */
174typedef struct RTFSFATDIR
175{
176 /** Core FAT object info. */
177 RTFSFATOBJ Core;
178 /** The VFS handle for this directory (for reference counting). */
179 RTVFSDIR hVfsSelf;
180 /** Open child objects (RTFSFATOBJ). */
181 RTLISTNODE OpenChildren;
182
183 /** Number of directory entries. */
184 uint32_t cEntries;
185
186 /** If fully buffered. */
187 bool fFullyBuffered;
188 /** Set if this is a linear root directory. */
189 bool fIsLinearRootDir;
190 /** The size of the memory paEntries points at. */
191 uint32_t cbAllocatedForEntries;
192
193 /** Pointer to the directory buffer.
194 * In fully buffering mode, this is the whole of the directory. Otherwise it's
195 * just a sector worth of buffers. */
196 PFATDIRENTRYUNION paEntries;
197 /** The disk offset corresponding to what paEntries points to.
198 * UINT64_MAX if notthing read into paEntries yet. */
199 uint64_t offEntriesOnDisk;
200 union
201 {
202 /** Data for the full buffered mode.
203 * No need to messing around with clusters here, as we only uses this for
204 * directories with a contiguous mapping on the disk.
205 * So, if we grow a directory in a non-contiguous manner, we have to switch
206 * to sector buffering on the fly. */
207 struct
208 {
209 /** Number of sectors mapped by paEntries and pbDirtySectors. */
210 uint32_t cSectors;
211 /** Number of dirty sectors. */
212 uint32_t cDirtySectors;
213 /** Dirty sector map. */
214 uint8_t *pbDirtySectors;
215 } Full;
216 /** The simple sector buffering.
217 * This only works for clusters, so no FAT12/16 root directory fun. */
218 struct
219 {
220 /** The directory offset, UINT32_MAX if invalid. */
221 uint32_t offInDir;
222 /** Dirty flag. */
223 bool fDirty;
224 } Simple;
225 } u;
226} RTFSFATDIR;
227/** Pointer to a FAT directory instance. */
228typedef RTFSFATDIR *PRTFSFATDIR;
229
230
231/**
232 * File allocation table cache entry.
233 */
234typedef struct RTFSFATCLUSTERMAPENTRY
235{
236 /** The byte offset into the fat, UINT32_MAX if invalid entry. */
237 uint32_t offFat;
238 /** Pointer to the data. */
239 uint8_t *pbData;
240 /** Dirty bitmap. Indexed by byte offset right shifted by
241 * RTFSFATCLUSTERMAPCACHE::cDirtyShift. */
242 uint64_t bmDirty;
243} RTFSFATCLUSTERMAPENTRY;
244/** Pointer to a file allocation table cache entry. */
245typedef RTFSFATCLUSTERMAPENTRY *PRTFSFATCLUSTERMAPENTRY;
246
247/**
248 * File allocation table cache.
249 */
250typedef struct RTFSFATCLUSTERMAPCACHE
251{
252 /** Number of cache entries. */
253 uint32_t cEntries;
254 /** The max size of data in a cache entry. */
255 uint32_t cbEntry;
256 /** Dirty bitmap shift count. */
257 uint32_t cDirtyShift;
258 /** The dirty cache line size (multiple of two). */
259 uint32_t cbDirtyLine;
260 /** The cache name. */
261 const char *pszName;
262 /** Cache entries. */
263 RTFSFATCLUSTERMAPENTRY aEntries[RT_FLEXIBLE_ARRAY];
264} RTFSFATCLUSTERMAPCACHE;
265/** Pointer to a FAT linear metadata cache. */
266typedef RTFSFATCLUSTERMAPCACHE *PRTFSFATCLUSTERMAPCACHE;
267
268
269/**
270 * BPB version.
271 */
272typedef enum RTFSFATBPBVER
273{
274 RTFSFATBPBVER_INVALID = 0,
275 RTFSFATBPBVER_NO_BPB,
276 RTFSFATBPBVER_DOS_2_0,
277 //RTFSFATBPBVER_DOS_3_2, - we don't try identify this one.
278 RTFSFATBPBVER_DOS_3_31,
279 RTFSFATBPBVER_EXT_28,
280 RTFSFATBPBVER_EXT_29,
281 RTFSFATBPBVER_FAT32_28,
282 RTFSFATBPBVER_FAT32_29,
283 RTFSFATBPBVER_END
284} RTFSFATBPBVER;
285
286
287/**
288 * A FAT volume.
289 */
290typedef struct RTFSFATVOL
291{
292 /** Handle to itself. */
293 RTVFS hVfsSelf;
294 /** The file, partition, or whatever backing the FAT volume. */
295 RTVFSFILE hVfsBacking;
296 /** The size of the backing thingy. */
297 uint64_t cbBacking;
298 /** Byte offset of the bootsector relative to the start of the file. */
299 uint64_t offBootSector;
300 /** The UTC offset in nanoseconds to use for this file system (FAT traditionally
301 * stores timestamps in local time).
302 * @remarks This may need improving later. */
303 int64_t offNanoUTC;
304 /** The UTC offset in minutes to use for this file system (FAT traditionally
305 * stores timestamps in local time).
306 * @remarks This may need improving later. */
307 int32_t offMinUTC;
308 /** Set if read-only mode. */
309 bool fReadOnly;
310 /** Media byte. */
311 uint8_t bMedia;
312 /** Reserved sectors. */
313 uint32_t cReservedSectors;
314 /** The BPB version. Gives us an idea of the FAT file system version. */
315 RTFSFATBPBVER enmBpbVersion;
316
317 /** Logical sector size. */
318 uint32_t cbSector;
319 /** The shift count for converting between sectors and bytes. */
320 uint8_t cSectorByteShift;
321 /** The shift count for converting between clusters and bytes. */
322 uint8_t cClusterByteShift;
323 /** The cluster size in bytes. */
324 uint32_t cbCluster;
325 /** The number of data clusters, including the two reserved ones. */
326 uint32_t cClusters;
327 /** The offset of the first cluster. */
328 uint64_t offFirstCluster;
329 /** The total size from the BPB, in bytes. */
330 uint64_t cbTotalSize;
331
332 /** The FAT type. */
333 RTFSFATTYPE enmFatType;
334
335 /** Number of FAT entries (clusters). */
336 uint32_t cFatEntries;
337 /** The size of a FAT, in bytes. */
338 uint32_t cbFat;
339 /** Number of FATs. */
340 uint32_t cFats;
341 /** The end of chain marker used by the formatter (FAT entry \#2). */
342 uint32_t idxEndOfChain;
343 /** The maximum last cluster supported by the FAT format. */
344 uint32_t idxMaxLastCluster;
345 /** FAT byte offsets. */
346 uint64_t aoffFats[8];
347 /** Pointer to the FAT (cluster map) cache. */
348 PRTFSFATCLUSTERMAPCACHE pFatCache;
349
350 /** The root directory byte offset. */
351 uint64_t offRootDir;
352 /** Root directory cluster, UINT32_MAX if not FAT32. */
353 uint32_t idxRootDirCluster;
354 /** Number of root directory entries, if fixed. UINT32_MAX for FAT32. */
355 uint32_t cRootDirEntries;
356 /** The size of the root directory, rounded up to the nearest sector size. */
357 uint32_t cbRootDir;
358 /** The root directory handle. */
359 RTVFSDIR hVfsRootDir;
360 /** The root directory instance data. */
361 PRTFSFATDIR pRootDir;
362
363 /** Serial number. */
364 uint32_t uSerialNo;
365 /** The stripped volume label, if included in EBPB. */
366 char szLabel[12];
367 /** The file system type from the EBPB (also stripped). */
368 char szType[9];
369 /** Number of FAT32 boot sector copies. */
370 uint8_t cBootSectorCopies;
371 /** FAT32 flags. */
372 uint16_t fFat32Flags;
373 /** Offset of the FAT32 boot sector copies, UINT64_MAX if none. */
374 uint64_t offBootSectorCopies;
375
376 /** The FAT32 info sector byte offset, UINT64_MAX if not present. */
377 uint64_t offFat32InfoSector;
378 /** The FAT32 info sector if offFat32InfoSector isn't UINT64_MAX. */
379 FAT32INFOSECTOR Fat32InfoSector;
380} RTFSFATVOL;
381/** Pointer to a FAT volume (VFS instance data). */
382typedef RTFSFATVOL *PRTFSFATVOL;
383/** Pointer to a const FAT volume (VFS instance data). */
384typedef RTFSFATVOL const *PCRTFSFATVOL;
385
386
387
388/*********************************************************************************************************************************
389* Global Variables *
390*********************************************************************************************************************************/
391/** Codepage 437 translation table with invalid 8.3 characters marked as 0xffff.
392 * @remarks The valid first 128 entries are 1:1 with unicode.
393 * @remarks Lower case characters are all marked invalid.
394 */
395static RTUTF16 g_awchFatCp437Chars[] =
396{ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */
397 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
398 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
399 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0xffff, 0xffff, 0xffff, 0x002d, 0xffff, 0xffff,
400 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
401 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
402 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0xffff, 0xffff, 0xffff, 0x005e, 0x005f,
403 0x0060, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
404 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x007e, 0xffff,
405 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
406 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
407 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
408 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
409 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
410 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
411 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
412 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
413};
414AssertCompileSize(g_awchFatCp437Chars, 256*2);
415
416
417/*********************************************************************************************************************************
418* Internal Functions *
419*********************************************************************************************************************************/
420static void rtFsFatDir_AddOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild);
421static void rtFsFatDir_RemoveOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild);
422static int rtFsFatDir_GetEntryForUpdate(PRTFSFATDIR pThis, uint32_t offEntryInDir, PFATDIRENTRY *ppDirEntry, uint32_t *puWriteLock);
423static int rtFsFatDir_PutEntryAfterUpdate(PRTFSFATDIR pThis, PFATDIRENTRY pDirEntry, uint32_t uWriteLock);
424static int rtFsFatDir_Flush(PRTFSFATDIR pThis);
425static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
426 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir, PRTFSFATDIR *ppDir);
427
428
429/**
430 * Convers a cluster to a disk offset.
431 *
432 * @returns Disk byte offset, UINT64_MAX on invalid cluster.
433 * @param pThis The FAT volume instance.
434 * @param idxCluster The cluster number.
435 */
436DECLINLINE(uint64_t) rtFsFatClusterToDiskOffset(PRTFSFATVOL pThis, uint32_t idxCluster)
437{
438 AssertReturn(idxCluster >= FAT_FIRST_DATA_CLUSTER, UINT64_MAX);
439 AssertReturn(idxCluster < pThis->cClusters, UINT64_MAX);
440 return (idxCluster - FAT_FIRST_DATA_CLUSTER) * (uint64_t)pThis->cbCluster
441 + pThis->offFirstCluster;
442}
443
444
445#ifdef RT_STRICT
446/**
447 * Assert chain consistency.
448 */
449static bool rtFsFatChain_AssertValid(PCRTFSFATCHAIN pChain)
450{
451 bool fRc = true;
452 uint32_t cParts = 0;
453 PRTFSFATCHAINPART pPart;
454 RTListForEach(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry)
455 cParts++;
456
457 uint32_t cExpected = (pChain->cClusters + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
458 AssertMsgStmt(cExpected == cParts, ("cExpected=%#x cParts=%#x\n", cExpected, cParts), fRc = false);
459 AssertMsgStmt(pChain->cbChain == (pChain->cClusters << pChain->cClusterByteShift),
460 ("cExpected=%#x cParts=%#x\n", cExpected, cParts), fRc = false);
461 return fRc;
462}
463#endif /* RT_STRICT */
464
465
466/**
467 * Initializes an empty cluster chain.
468 *
469 * @param pChain The chain.
470 * @param pVol The volume.
471 */
472static void rtFsFatChain_InitEmpty(PRTFSFATCHAIN pChain, PRTFSFATVOL pVol)
473{
474 pChain->cbCluster = pVol->cbCluster;
475 pChain->cClusterByteShift = pVol->cClusterByteShift;
476 pChain->cbChain = 0;
477 pChain->cClusters = 0;
478 RTListInit(&pChain->ListParts);
479}
480
481
482/**
483 * Deletes a chain, freeing it's resources.
484 *
485 * @param pChain The chain.
486 */
487static void rtFsFatChain_Delete(PRTFSFATCHAIN pChain)
488{
489 Assert(RT_IS_POWER_OF_TWO(pChain->cbCluster));
490 Assert(RT_BIT_32(pChain->cClusterByteShift) == pChain->cbCluster);
491
492 PRTFSFATCHAINPART pPart = RTListRemoveLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
493 while (pPart)
494 {
495 RTMemFree(pPart);
496 pPart = RTListRemoveLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
497 }
498
499 pChain->cbChain = 0;
500 pChain->cClusters = 0;
501}
502
503
504/**
505 * Appends a cluster to a cluster chain.
506 *
507 * @returns IPRT status code.
508 * @param pChain The chain.
509 * @param idxCluster The cluster to append.
510 */
511static int rtFsFatChain_Append(PRTFSFATCHAIN pChain, uint32_t idxCluster)
512{
513 PRTFSFATCHAINPART pPart;
514 uint32_t idxLast = pChain->cClusters % RTFSFATCHAINPART_ENTRIES;
515 if (idxLast != 0)
516 pPart = RTListGetLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
517 else
518 {
519 pPart = (PRTFSFATCHAINPART)RTMemAllocZ(sizeof(*pPart));
520 if (!pPart)
521 return VERR_NO_MEMORY;
522 RTListAppend(&pChain->ListParts, &pPart->ListEntry);
523 }
524 pPart->aEntries[idxLast] = idxCluster;
525 pChain->cClusters++;
526 pChain->cbChain += pChain->cbCluster;
527 return VINF_SUCCESS;
528}
529
530
531/**
532 * Reduces the number of clusters in the chain to @a cClusters.
533 *
534 * @param pChain The chain.
535 * @param cClustersNew The new cluster count. Must be equal or smaller to
536 * the current number of clusters.
537 */
538static void rtFsFatChain_Shrink(PRTFSFATCHAIN pChain, uint32_t cClustersNew)
539{
540 uint32_t cOldParts = (pChain->cClusters + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
541 uint32_t cNewParts = (cClustersNew + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
542 Assert(cOldParts >= cNewParts);
543 while (cOldParts-- > cNewParts)
544 RTMemFree(RTListRemoveLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry));
545 pChain->cClusters = cClustersNew;
546 pChain->cbChain = cClustersNew << pChain->cClusterByteShift;
547 Assert(rtFsFatChain_AssertValid(pChain));
548}
549
550
551
552/**
553 * Converts a file offset to a disk offset.
554 *
555 * The disk offset is only valid until the end of the cluster it is within.
556 *
557 * @returns Disk offset. UINT64_MAX if invalid file offset.
558 * @param pChain The chain.
559 * @param offFile The file offset.
560 * @param pVol The volume.
561 */
562static uint64_t rtFsFatChain_FileOffsetToDiskOff(PCRTFSFATCHAIN pChain, uint32_t offFile, PCRTFSFATVOL pVol)
563{
564 uint32_t idxCluster = offFile >> pChain->cClusterByteShift;
565 if (idxCluster < pChain->cClusters)
566 {
567 PRTFSFATCHAINPART pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
568 while (idxCluster >= RTFSFATCHAINPART_ENTRIES)
569 {
570 idxCluster -= RTFSFATCHAINPART_ENTRIES;
571 pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
572 }
573 return pVol->offFirstCluster
574 + ((uint64_t)(pPart->aEntries[idxCluster] - FAT_FIRST_DATA_CLUSTER) << pChain->cClusterByteShift)
575 + (offFile & (pChain->cbCluster - 1));
576 }
577 return UINT64_MAX;
578}
579
580
581/**
582 * Checks if the cluster chain is contiguous on the disk.
583 *
584 * @returns true / false.
585 * @param pChain The chain.
586 */
587static bool rtFsFatChain_IsContiguous(PCRTFSFATCHAIN pChain)
588{
589 if (pChain->cClusters <= 1)
590 return true;
591
592 PRTFSFATCHAINPART pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
593 uint32_t idxNext = pPart->aEntries[0];
594 uint32_t cLeft = pChain->cClusters;
595 for (;;)
596 {
597 uint32_t const cInPart = RT_MIN(cLeft, RTFSFATCHAINPART_ENTRIES);
598 for (uint32_t iPart = 0; iPart < cInPart; iPart++)
599 if (pPart->aEntries[iPart] == idxNext)
600 idxNext++;
601 else
602 return false;
603 cLeft -= cInPart;
604 if (!cLeft)
605 return true;
606 pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
607 }
608}
609
610
611/**
612 * Gets a cluster array index.
613 *
614 * This works the chain thing as an indexed array.
615 *
616 * @returns The cluster number, UINT32_MAX if out of bounds.
617 * @param pChain The chain.
618 * @param idx The index.
619 */
620static uint32_t rtFsFatChain_GetClusterByIndex(PCRTFSFATCHAIN pChain, uint32_t idx)
621{
622 if (idx < pChain->cClusters)
623 {
624 /*
625 * In the first part?
626 */
627 PRTFSFATCHAINPART pPart;
628 if (idx < RTFSFATCHAINPART_ENTRIES)
629 {
630 pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
631 return pPart->aEntries[idx];
632 }
633
634 /*
635 * In the last part?
636 */
637 uint32_t cParts = (pChain->cClusters + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
638 uint32_t idxPart = idx / RTFSFATCHAINPART_ENTRIES;
639 uint32_t idxInPart = idx % RTFSFATCHAINPART_ENTRIES;
640 if (idxPart + 1 == cParts)
641 pPart = RTListGetLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
642 else
643 {
644 /*
645 * No, do linear search from the start, skipping the first part.
646 */
647 pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
648 while (idxPart-- > 1)
649 pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
650 }
651
652 return pPart->aEntries[idxInPart];
653 }
654 return UINT32_MAX;
655}
656
657
658/**
659 * Gets the first cluster.
660 *
661 * @returns The cluster number, UINT32_MAX if empty
662 * @param pChain The chain.
663 */
664static uint32_t rtFsFatChain_GetFirstCluster(PCRTFSFATCHAIN pChain)
665{
666 if (pChain->cClusters > 0)
667 {
668 PRTFSFATCHAINPART pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
669 return pPart->aEntries[0];
670 }
671 return UINT32_MAX;
672}
673
674
675
676/**
677 * Gets the last cluster.
678 *
679 * @returns The cluster number, UINT32_MAX if empty
680 * @param pChain The chain.
681 */
682static uint32_t rtFsFatChain_GetLastCluster(PCRTFSFATCHAIN pChain)
683{
684 if (pChain->cClusters > 0)
685 {
686 PRTFSFATCHAINPART pPart = RTListGetLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
687 return pPart->aEntries[(pChain->cClusters - 1) % RTFSFATCHAINPART_ENTRIES];
688 }
689 return UINT32_MAX;
690}
691
692
693/**
694 * Creates a cache for the file allocation table (cluster map).
695 *
696 * @returns Pointer to the cache.
697 * @param pThis The FAT volume instance.
698 * @param pbFirst512FatBytes The first 512 bytes of the first FAT.
699 */
700static int rtFsFatClusterMap_Create(PRTFSFATVOL pThis, uint8_t const *pbFirst512FatBytes, PRTERRINFO pErrInfo)
701{
702 Assert(RT_ALIGN_32(pThis->cbFat, pThis->cbSector) == pThis->cbFat);
703 Assert(pThis->cbFat != 0);
704
705 /*
706 * Figure the cache size. Keeping it _very_ simple for now as we just need
707 * something that works, not anything the performs like crazy.
708 */
709 uint32_t cEntries;
710 uint32_t cbEntry = pThis->cbFat;
711 if (cbEntry <= _512K)
712 cEntries = 1;
713 else
714 {
715 Assert(pThis->cbSector < _512K / 8);
716 cEntries = 8;
717 cbEntry = pThis->cbSector;
718 }
719
720 /*
721 * Allocate and initialize it all.
722 */
723 PRTFSFATCLUSTERMAPCACHE pCache;
724 pThis->pFatCache = pCache = (PRTFSFATCLUSTERMAPCACHE)RTMemAllocZ(RT_OFFSETOF(RTFSFATCLUSTERMAPCACHE, aEntries[cEntries]));
725 if (!pCache)
726 return RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "Failed to allocate FAT cache");
727 pCache->cEntries = cEntries;
728 pCache->cbEntry = cbEntry;
729
730 unsigned i = cEntries;
731 while (i-- > 0)
732 {
733 pCache->aEntries[i].pbData = (uint8_t *)RTMemAlloc(cbEntry);
734 if (pCache->aEntries[i].pbData == NULL)
735 {
736 for (i++; i < cEntries; i++)
737 RTMemFree(pCache->aEntries[i].pbData);
738 RTMemFree(pCache);
739 return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate FAT cache entry (%#x bytes)", cbEntry);
740 }
741
742 pCache->aEntries[i].offFat = UINT32_MAX;
743 pCache->aEntries[i].bmDirty = 0;
744 }
745
746 /*
747 * Calc the dirty shift factor.
748 */
749 cbEntry /= 64;
750 if (cbEntry < pThis->cbSector)
751 cbEntry = pThis->cbSector;
752
753 pCache->cDirtyShift = 1;
754 pCache->cbDirtyLine = 1;
755 while (pCache->cbDirtyLine < cbEntry)
756 {
757 pCache->cDirtyShift++;
758 pCache->cbDirtyLine <<= 1;
759 }
760
761 /*
762 * Fill the cache if single entry or entry size is 512.
763 */
764 if (pCache->cEntries == 1 || pCache->cbEntry == 512)
765 {
766 memcpy(pCache->aEntries[0].pbData, pbFirst512FatBytes, RT_MIN(512, pCache->cbEntry));
767 if (pCache->cbEntry > 512)
768 {
769 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0] + 512,
770 &pCache->aEntries[0].pbData[512], pCache->cbEntry - 512, NULL);
771 if (RT_FAILURE(rc))
772 return RTErrInfoSet(pErrInfo, rc, "Error reading FAT into memory");
773 }
774 pCache->aEntries[0].offFat = 0;
775 pCache->aEntries[0].bmDirty = 0;
776 }
777
778 return VINF_SUCCESS;
779}
780
781
782/**
783 * Worker for rtFsFatClusterMap_Flush and rtFsFatClusterMap_FlushEntry.
784 *
785 * @returns IPRT status code. On failure, we're currently kind of screwed.
786 * @param pThis The FAT volume instance.
787 * @param iFirstEntry Entry to start flushing at.
788 * @param iLastEntry Last entry to flush.
789 */
790static int rtFsFatClusterMap_FlushWorker(PRTFSFATVOL pThis, uint32_t const iFirstEntry, uint32_t const iLastEntry)
791{
792 PRTFSFATCLUSTERMAPCACHE pCache = pThis->pFatCache;
793
794
795 /*
796 * Walk the cache entries, accumulating segments to flush.
797 */
798 int rc = VINF_SUCCESS;
799 uint64_t off = UINT64_MAX;
800 uint64_t offEdge = UINT64_MAX;
801 RTSGSEG aSgSegs[8];
802 RTSGBUF SgBuf;
803 RTSgBufInit(&SgBuf, aSgSegs, RT_ELEMENTS(aSgSegs));
804 SgBuf.cSegs = 0; /** @todo RTSgBuf API is stupid, make it smarter. */
805
806 for (uint32_t iFatCopy = 0; iFatCopy < pThis->cFats; iFatCopy++)
807 {
808 for (uint32_t iEntry = iFirstEntry; iEntry <= iLastEntry; iEntry++)
809 {
810 uint64_t bmDirty = pCache->aEntries[iEntry].bmDirty;
811 if ( bmDirty != 0
812 && pCache->aEntries[iEntry].offFat != UINT32_MAX)
813 {
814 uint32_t offEntry = 0;
815 uint64_t iDirtyLine = 1;
816 while (offEntry < pCache->cbEntry)
817 {
818 if (pCache->aEntries[iEntry].bmDirty & iDirtyLine)
819 {
820 /*
821 * Found dirty cache line.
822 */
823 uint64_t offDirtyLine = pThis->aoffFats[iFatCopy] + pCache->aEntries[iEntry].offFat + offEntry;
824
825 /* Can we simply extend the last segment? */
826 if ( offDirtyLine == offEdge
827 && offEntry)
828 {
829 Assert(SgBuf.cSegs > 0);
830 Assert( (uintptr_t)aSgSegs[SgBuf.cSegs - 1].pvSeg + aSgSegs[SgBuf.cSegs - 1].cbSeg
831 == (uintptr_t)&pCache->aEntries[iEntry].pbData[offEntry]);
832 aSgSegs[SgBuf.cSegs - 1].cbSeg += pCache->cbDirtyLine;
833 offEdge += pCache->cbDirtyLine;
834 }
835 else
836 {
837 /* Starting new job? */
838 if (off == UINT64_MAX)
839 {
840 off = offDirtyLine;
841 Assert(SgBuf.cSegs == 0);
842 }
843 /* flush if not adjacent or if we're out of segments. */
844 else if ( offDirtyLine != offEdge
845 || SgBuf.cSegs >= RT_ELEMENTS(aSgSegs))
846 {
847 int rc2 = RTVfsFileSgWrite(pThis->hVfsBacking, off, &SgBuf, true /*fBlocking*/, NULL);
848 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
849 rc = rc2;
850 RTSgBufReset(&SgBuf);
851 SgBuf.cSegs = 0;
852 off = offDirtyLine;
853 }
854
855 /* Append segment. */
856 aSgSegs[SgBuf.cSegs].cbSeg = pCache->cbDirtyLine;
857 aSgSegs[SgBuf.cSegs].pvSeg = &pCache->aEntries[iEntry].pbData[offEntry];
858 SgBuf.cSegs++;
859 offEdge = offDirtyLine + pCache->cbDirtyLine;
860 }
861
862 bmDirty &= ~iDirtyLine;
863 if (!bmDirty)
864 break;
865 }
866 iDirtyLine <<= 1;
867 offEntry += pCache->cbDirtyLine;
868 }
869 Assert(!bmDirty);
870 }
871 }
872 }
873
874 /*
875 * Final flush job.
876 */
877 if (SgBuf.cSegs > 0)
878 {
879 int rc2 = RTVfsFileSgWrite(pThis->hVfsBacking, off, &SgBuf, true /*fBlocking*/, NULL);
880 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
881 rc = rc2;
882 }
883
884 /*
885 * Clear the dirty flags on success.
886 */
887 if (RT_SUCCESS(rc))
888 for (uint32_t iEntry = iFirstEntry; iEntry <= iLastEntry; iEntry++)
889 pCache->aEntries[iEntry].bmDirty = 0;
890
891 return rc;
892}
893
894
895/**
896 * Flushes out all dirty lines in the entire file allocation table cache.
897 *
898 * @returns IPRT status code. On failure, we're currently kind of screwed.
899 * @param pThis The FAT volume instance.
900 */
901static int rtFsFatClusterMap_Flush(PRTFSFATVOL pThis)
902{
903 return rtFsFatClusterMap_FlushWorker(pThis, 0, pThis->pFatCache->cEntries - 1);
904}
905
906
907#if 0 /* unused */
908/**
909 * Flushes out all dirty lines in the file allocation table (cluster map) cache.
910 *
911 * This is typically called prior to reusing the cache entry.
912 *
913 * @returns IPRT status code. On failure, we're currently kind of screwed.
914 * @param pThis The FAT volume instance.
915 * @param iEntry The cache entry to flush.
916 */
917static int rtFsFatClusterMap_FlushEntry(PRTFSFATVOL pThis, uint32_t iEntry)
918{
919 return rtFsFatClusterMap_FlushWorker(pThis, iEntry, iEntry);
920}
921#endif
922
923
924/**
925 * Destroys the file allcation table cache, first flushing any dirty lines.
926 *
927 * @returns IRPT status code from flush (we've destroyed it regardless of the
928 * status code).
929 * @param pThis The FAT volume instance which cluster map shall be
930 * destroyed.
931 */
932static int rtFsFatClusterMap_Destroy(PRTFSFATVOL pThis)
933{
934 int rc = VINF_SUCCESS;
935 PRTFSFATCLUSTERMAPCACHE pCache = pThis->pFatCache;
936 if (pCache)
937 {
938 /* flush stuff. */
939 rc = rtFsFatClusterMap_Flush(pThis);
940
941 /* free everything. */
942 uint32_t i = pCache->cEntries;
943 while (i-- > 0)
944 {
945 RTMemFree(pCache->aEntries[i].pbData);
946 pCache->aEntries[i].pbData = NULL;
947 }
948 pCache->cEntries = 0;
949 RTMemFree(pCache);
950
951 pThis->pFatCache = NULL;
952 }
953
954 return rc;
955}
956
957
958/**
959 * Worker for rtFsFatClusterMap_ReadClusterChain handling FAT12.
960 */
961static int rtFsFatClusterMap_Fat12_ReadClusterChain(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, uint32_t idxCluster,
962 PRTFSFATCHAIN pChain)
963{
964 /* ASSUME that for FAT12 we cache the whole FAT in a single entry. That
965 way we don't need to deal with entries in different sectors and whatnot. */
966 AssertReturn(pFatCache->cEntries == 1, VERR_INTERNAL_ERROR_4);
967 AssertReturn(pFatCache->cbEntry == pVol->cbFat, VERR_INTERNAL_ERROR_4);
968 AssertReturn(pFatCache->aEntries[0].offFat == 0, VERR_INTERNAL_ERROR_4);
969
970 /* Special case for empty files. */
971 if (idxCluster == 0)
972 return VINF_SUCCESS;
973
974 /* Work cluster by cluster. */
975 uint8_t const *pbFat = pFatCache->aEntries[0].pbData;
976 for (;;)
977 {
978 /* Validate the cluster, checking for end of file. */
979 if ( idxCluster >= pVol->cClusters
980 || idxCluster < FAT_FIRST_DATA_CLUSTER)
981 {
982 if (idxCluster >= FAT_FIRST_FAT12_EOC)
983 return VINF_SUCCESS;
984 return VERR_VFS_BOGUS_OFFSET;
985 }
986
987 /* Add cluster to chain. */
988 int rc = rtFsFatChain_Append(pChain, idxCluster);
989 if (RT_FAILURE(rc))
990 return rc;
991
992 /* Next cluster. */
993 bool fOdd = idxCluster & 1;
994 uint32_t offFat = idxCluster * 3 / 2;
995 idxCluster = RT_MAKE_U16(pbFat[offFat], pbFat[offFat + 1]);
996 if (fOdd)
997 idxCluster >>= 4;
998 else
999 idxCluster &= 0x0fff;
1000 }
1001}
1002
1003
1004/**
1005 * Worker for rtFsFatClusterMap_ReadClusterChain handling FAT16.
1006 */
1007static int rtFsFatClusterMap_Fat16_ReadClusterChain(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, uint32_t idxCluster,
1008 PRTFSFATCHAIN pChain)
1009{
1010 RT_NOREF(pFatCache, pVol, idxCluster, pChain);
1011 return VERR_NOT_IMPLEMENTED;
1012}
1013
1014
1015/**
1016 * Worker for rtFsFatClusterMap_ReadClusterChain handling FAT32.
1017 */
1018static int rtFsFatClusterMap_Fat32_ReadClusterChain(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, uint32_t idxCluster,
1019 PRTFSFATCHAIN pChain)
1020{
1021 RT_NOREF(pFatCache, pVol, idxCluster, pChain);
1022 return VERR_NOT_IMPLEMENTED;
1023}
1024
1025
1026/**
1027 * Reads a cluster chain into memory
1028 *
1029 * @returns IPRT status code.
1030 * @param pThis The FAT volume instance.
1031 * @param idxFirstCluster The first cluster.
1032 * @param pChain The chain element to read into (and thereby
1033 * initialize).
1034 */
1035static int rtFsFatClusterMap_ReadClusterChain(PRTFSFATVOL pThis, uint32_t idxFirstCluster, PRTFSFATCHAIN pChain)
1036{
1037 pChain->cbCluster = pThis->cbCluster;
1038 pChain->cClusterByteShift = pThis->cClusterByteShift;
1039 pChain->cClusters = 0;
1040 pChain->cbChain = 0;
1041 RTListInit(&pChain->ListParts);
1042 switch (pThis->enmFatType)
1043 {
1044 case RTFSFATTYPE_FAT12: return rtFsFatClusterMap_Fat12_ReadClusterChain(pThis->pFatCache, pThis, idxFirstCluster, pChain);
1045 case RTFSFATTYPE_FAT16: return rtFsFatClusterMap_Fat16_ReadClusterChain(pThis->pFatCache, pThis, idxFirstCluster, pChain);
1046 case RTFSFATTYPE_FAT32: return rtFsFatClusterMap_Fat32_ReadClusterChain(pThis->pFatCache, pThis, idxFirstCluster, pChain);
1047 default:
1048 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
1049 }
1050}
1051
1052
1053/**
1054 * Sets bmDirty for entry @a iEntry.
1055 *
1056 * @param pFatCache The FAT cache.
1057 * @param iEntry The cache entry.
1058 * @param offEntry The offset into the cache entry that was dirtied.
1059 */
1060DECLINLINE(void) rtFsFatClusterMap_SetDirtyByte(PRTFSFATCLUSTERMAPCACHE pFatCache, uint32_t iEntry, uint32_t offEntry)
1061{
1062 uint8_t iLine = offEntry / pFatCache->cbDirtyLine;
1063 pFatCache->aEntries[iEntry].bmDirty |= RT_BIT_64(iLine);
1064}
1065
1066
1067/** Sets a FAT12 cluster value. */
1068static int rtFsFatClusterMap_SetCluster12(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol,
1069 uint32_t idxCluster, uint32_t uValue)
1070{
1071 /* ASSUME that for FAT12 we cache the whole FAT in a single entry. That
1072 way we don't need to deal with entries in different sectors and whatnot. */
1073 AssertReturn(pFatCache->cEntries == 1, VERR_INTERNAL_ERROR_4);
1074 AssertReturn(pFatCache->cbEntry == pVol->cbFat, VERR_INTERNAL_ERROR_4);
1075 AssertReturn(pFatCache->aEntries[0].offFat == 0, VERR_INTERNAL_ERROR_4);
1076 AssertReturn(uValue < 0x1000, VERR_INTERNAL_ERROR_2);
1077
1078 /* Make the change. */
1079 uint8_t *pbFat = pFatCache->aEntries[0].pbData;
1080 uint32_t offFat = idxCluster * 3 / 2;
1081 if (idxCluster & 1)
1082 {
1083 pbFat[offFat] = ((uint8_t)0x0f & pbFat[offFat]) | ((uint8_t)uValue << 4);
1084 pbFat[offFat + 1] = (uint8_t)(uValue >> 4);
1085 }
1086 else
1087 {
1088 pbFat[offFat] = (uint8_t)uValue;
1089 pbFat[offFat + 1] = ((uint8_t)0xf0 & pbFat[offFat + 1]) | (uint8_t)(uValue >> 8);
1090 }
1091
1092 /* Update the dirty bits. */
1093 rtFsFatClusterMap_SetDirtyByte(pFatCache, 0, offFat);
1094 rtFsFatClusterMap_SetDirtyByte(pFatCache, 0, offFat + 1);
1095
1096 return VINF_SUCCESS;
1097}
1098
1099
1100/** Sets a FAT16 cluster value. */
1101static int rtFsFatClusterMap_SetCluster16(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol,
1102 uint32_t idxCluster, uint32_t uValue)
1103{
1104 AssertReturn(uValue < 0x10000, VERR_INTERNAL_ERROR_2);
1105 RT_NOREF(pFatCache, pVol, idxCluster, uValue);
1106 return VERR_NOT_IMPLEMENTED;
1107}
1108
1109
1110/** Sets a FAT32 cluster value. */
1111static int rtFsFatClusterMap_SetCluster32(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol,
1112 uint32_t idxCluster, uint32_t uValue)
1113{
1114 AssertReturn(uValue < 0x10000000, VERR_INTERNAL_ERROR_2);
1115 RT_NOREF(pFatCache, pVol, idxCluster, uValue);
1116 return VERR_NOT_IMPLEMENTED;
1117}
1118
1119
1120/**
1121 * Marks the cluster @a idxCluster as the end of the cluster chain.
1122 *
1123 * @returns IPRT status code
1124 * @param pThis The FAT volume instance.
1125 * @param idxCluster The cluster to end the chain with.
1126 */
1127static int rtFsFatClusterMap_SetEndOfChain(PRTFSFATVOL pThis, uint32_t idxCluster)
1128{
1129 AssertReturn(idxCluster >= FAT_FIRST_DATA_CLUSTER, VERR_VFS_BOGUS_OFFSET);
1130 AssertMsgReturn(idxCluster < pThis->cClusters, ("idxCluster=%#x cClusters=%#x\n", idxCluster, pThis->cClusters),
1131 VERR_VFS_BOGUS_OFFSET);
1132 switch (pThis->enmFatType)
1133 {
1134 case RTFSFATTYPE_FAT12: return rtFsFatClusterMap_SetCluster12(pThis->pFatCache, pThis, idxCluster, FAT_FIRST_FAT12_EOC);
1135 case RTFSFATTYPE_FAT16: return rtFsFatClusterMap_SetCluster16(pThis->pFatCache, pThis, idxCluster, FAT_FIRST_FAT16_EOC);
1136 case RTFSFATTYPE_FAT32: return rtFsFatClusterMap_SetCluster32(pThis->pFatCache, pThis, idxCluster, FAT_FIRST_FAT32_EOC);
1137 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
1138 }
1139}
1140
1141
1142/**
1143 * Marks the cluster @a idxCluster as free.
1144 * @returns IPRT status code
1145 * @param pThis The FAT volume instance.
1146 * @param idxCluster The cluster to free.
1147 */
1148static int rtFsFatClusterMap_FreeCluster(PRTFSFATVOL pThis, uint32_t idxCluster)
1149{
1150 AssertReturn(idxCluster >= FAT_FIRST_DATA_CLUSTER, VERR_VFS_BOGUS_OFFSET);
1151 AssertReturn(idxCluster < pThis->cClusters, VERR_VFS_BOGUS_OFFSET);
1152 switch (pThis->enmFatType)
1153 {
1154 case RTFSFATTYPE_FAT12: return rtFsFatClusterMap_SetCluster12(pThis->pFatCache, pThis, idxCluster, 0);
1155 case RTFSFATTYPE_FAT16: return rtFsFatClusterMap_SetCluster16(pThis->pFatCache, pThis, idxCluster, 0);
1156 case RTFSFATTYPE_FAT32: return rtFsFatClusterMap_SetCluster32(pThis->pFatCache, pThis, idxCluster, 0);
1157 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
1158 }
1159}
1160
1161
1162/**
1163 * Worker for rtFsFatClusterMap_AllocateCluster that handles FAT12.
1164 */
1165static int rtFsFatClusterMap_AllocateCluster12(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol,
1166 uint32_t idxPrevCluster, uint32_t *pidxCluster)
1167{
1168 /* ASSUME that for FAT12 we cache the whole FAT in a single entry. That
1169 way we don't need to deal with entries in different sectors and whatnot. */
1170 AssertReturn(pFatCache->cEntries == 1, VERR_INTERNAL_ERROR_4);
1171 AssertReturn(pFatCache->cbEntry == pVol->cbFat, VERR_INTERNAL_ERROR_4);
1172 AssertReturn(pFatCache->aEntries[0].offFat == 0, VERR_INTERNAL_ERROR_4);
1173
1174 /*
1175 * Check that the previous cluster is a valid chain end.
1176 */
1177 uint8_t *pbFat = pFatCache->aEntries[0].pbData;
1178 uint32_t offFatPrev;
1179 if (idxPrevCluster != UINT32_MAX)
1180 {
1181 offFatPrev = idxPrevCluster * 3 / 2;
1182 uint32_t idxPrevValue;
1183 if (idxPrevCluster & 1)
1184 idxPrevValue = (pbFat[offFatPrev] >> 4) | ((uint32_t)pbFat[offFatPrev + 1] << 4);
1185 else
1186 idxPrevValue = pbFat[offFatPrev] | ((uint32_t)(pbFat[offFatPrev + 1] & 0x0f) << 8);
1187 AssertReturn(idxPrevValue >= FAT_FIRST_FAT12_EOC, VERR_VFS_BOGUS_OFFSET);
1188 }
1189 else
1190 offFatPrev = UINT32_MAX;
1191
1192 /*
1193 * Search cluster by cluster from the start (it's small, so easy trumps
1194 * complicated optimizations).
1195 */
1196 uint32_t idxCluster = FAT_FIRST_DATA_CLUSTER;
1197 uint32_t offFat = 3;
1198 while (idxCluster < pVol->cClusters)
1199 {
1200 if (idxCluster & 1)
1201 {
1202 if ( (pbFat[offFat] & 0xf0) != 0
1203 || pbFat[offFat + 1] != 0)
1204 {
1205 offFat += 2;
1206 idxCluster++;
1207 continue;
1208 }
1209
1210 /* Set EOC. */
1211 pbFat[offFat] |= 0xf0;
1212 pbFat[offFat + 1] = 0xff;
1213 }
1214 else if ( pbFat[offFat]
1215 || pbFat[offFat + 1] & 0x0f)
1216 {
1217 offFat += 1;
1218 idxCluster++;
1219 continue;
1220 }
1221 else
1222 {
1223 /* Set EOC. */
1224 pbFat[offFat] = 0xff;
1225 pbFat[offFat + 1] |= 0x0f;
1226 }
1227
1228 /* Update the dirty bits. */
1229 rtFsFatClusterMap_SetDirtyByte(pFatCache, 0, offFat);
1230 rtFsFatClusterMap_SetDirtyByte(pFatCache, 0, offFat + 1);
1231
1232 /* Chain it on the previous cluster. */
1233 if (idxPrevCluster != UINT32_MAX)
1234 {
1235 if (idxPrevCluster & 1)
1236 {
1237 pbFat[offFatPrev] = (pbFat[offFatPrev] & (uint8_t)0x0f) | (uint8_t)(idxCluster << 4);
1238 pbFat[offFatPrev + 1] = (uint8_t)(idxCluster >> 4);
1239 }
1240 else
1241 {
1242 pbFat[offFatPrev] = (uint8_t)idxCluster;
1243 pbFat[offFatPrev + 1] = (pbFat[offFatPrev] & (uint8_t)0xf0) | ((uint8_t)(idxCluster >> 8) & (uint8_t)0x0f);
1244 }
1245 rtFsFatClusterMap_SetDirtyByte(pFatCache, 0, offFatPrev);
1246 rtFsFatClusterMap_SetDirtyByte(pFatCache, 0, offFatPrev + 1);
1247 }
1248
1249 *pidxCluster = idxCluster;
1250 return VINF_SUCCESS;
1251 }
1252
1253 return VERR_DISK_FULL;
1254}
1255
1256
1257/**
1258 * Worker for rtFsFatClusterMap_AllocateCluster that handles FAT16.
1259 */
1260static int rtFsFatClusterMap_AllocateCluster16(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol,
1261 uint32_t idxPrevCluster, uint32_t *pidxCluster)
1262{
1263 RT_NOREF(pFatCache, pVol, idxPrevCluster, pidxCluster);
1264 return VERR_NOT_IMPLEMENTED;
1265}
1266
1267
1268/**
1269 * Worker for rtFsFatClusterMap_AllocateCluster that handles FAT32.
1270 */
1271static int rtFsFatClusterMap_AllocateCluster32(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol,
1272 uint32_t idxPrevCluster, uint32_t *pidxCluster)
1273{
1274 RT_NOREF(pFatCache, pVol, idxPrevCluster, pidxCluster);
1275 return VERR_NOT_IMPLEMENTED;
1276}
1277
1278
1279/**
1280 * Allocates a cluster an appends it to the chain given by @a idxPrevCluster.
1281 *
1282 * @returns IPRT status code.
1283 * @retval VERR_DISK_FULL if no more available clusters.
1284 * @param pThis The FAT volume instance.
1285 * @param idxPrevCluster The previous cluster, UINT32_MAX if first.
1286 * @param pidxCluster Where to return the cluster number on success.
1287 */
1288static int rtFsFatClusterMap_AllocateCluster(PRTFSFATVOL pThis, uint32_t idxPrevCluster, uint32_t *pidxCluster)
1289{
1290 AssertReturn(idxPrevCluster == UINT32_MAX || (idxPrevCluster >= FAT_FIRST_DATA_CLUSTER && idxPrevCluster < pThis->cClusters),
1291 VERR_INTERNAL_ERROR_5);
1292 *pidxCluster = UINT32_MAX;
1293 switch (pThis->enmFatType)
1294 {
1295 case RTFSFATTYPE_FAT12: return rtFsFatClusterMap_AllocateCluster12(pThis->pFatCache, pThis, idxPrevCluster, pidxCluster);
1296 case RTFSFATTYPE_FAT16: return rtFsFatClusterMap_AllocateCluster16(pThis->pFatCache, pThis, idxPrevCluster, pidxCluster);
1297 case RTFSFATTYPE_FAT32: return rtFsFatClusterMap_AllocateCluster32(pThis->pFatCache, pThis, idxPrevCluster, pidxCluster);
1298 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
1299 }
1300}
1301
1302
1303/**
1304 * Allocates clusters.
1305 *
1306 * Will free the clusters if it fails to allocate all of them.
1307 *
1308 * @returns IPRT status code.
1309 * @param pThis The FAT volume instance.
1310 * @param pChain The chain.
1311 * @param cClusters Number of clusters to add to the chain.
1312 */
1313static int rtFsFatClusterMap_AllocateMoreClusters(PRTFSFATVOL pThis, PRTFSFATCHAIN pChain, uint32_t cClusters)
1314{
1315 int rc = VINF_SUCCESS;
1316 uint32_t const cOldClustersInChain = pChain->cClusters;
1317 uint32_t const idxOldLastCluster = rtFsFatChain_GetLastCluster(pChain);
1318 uint32_t idxPrevCluster = idxOldLastCluster;
1319 uint32_t iCluster = 0;
1320 while (iCluster < cClusters)
1321 {
1322 uint32_t idxCluster;
1323 rc = rtFsFatClusterMap_AllocateCluster(pThis, idxPrevCluster, &idxCluster);
1324 if (RT_SUCCESS(rc))
1325 {
1326 rc = rtFsFatChain_Append(pChain, idxCluster);
1327 if (RT_SUCCESS(rc))
1328 {
1329 /* next */
1330 iCluster++;
1331 continue;
1332 }
1333
1334 /* Bail out, freeing any clusters we've managed to allocate by now. */
1335 rtFsFatClusterMap_FreeCluster(pThis, idxCluster);
1336 }
1337 if (idxOldLastCluster != UINT32_MAX)
1338 rtFsFatClusterMap_SetEndOfChain(pThis, idxOldLastCluster);
1339 while (iCluster-- > 0)
1340 rtFsFatClusterMap_FreeCluster(pThis, rtFsFatChain_GetClusterByIndex(pChain, cOldClustersInChain + iCluster));
1341 rtFsFatChain_Shrink(pChain, iCluster);
1342 break;
1343 }
1344 return rc;
1345}
1346
1347
1348
1349/**
1350 * Converts a FAT timestamp into an IPRT timesspec.
1351 *
1352 * @param pTimeSpec Where to return the IRPT time.
1353 * @param uDate The date part of the FAT timestamp.
1354 * @param uTime The time part of the FAT timestamp.
1355 * @param cCentiseconds Centiseconds part if applicable (0 otherwise).
1356 * @param pVol The volume.
1357 */
1358static void rtFsFatDateTime2TimeSpec(PRTTIMESPEC pTimeSpec, uint16_t uDate, uint16_t uTime,
1359 uint8_t cCentiseconds, PCRTFSFATVOL pVol)
1360{
1361 RTTIME Time;
1362 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
1363 Time.offUTC = 0;
1364 Time.i32Year = 1980 + (uDate >> 9);
1365 Time.u8Month = RT_MAX((uDate >> 5) & 0xf, 1);
1366 Time.u8MonthDay = RT_MAX(uDate & 0x1f, 1);
1367 Time.u8WeekDay = UINT8_MAX;
1368 Time.u16YearDay = 0;
1369 Time.u8Hour = uTime >> 11;
1370 Time.u8Minute = (uTime >> 5) & 0x3f;
1371 Time.u8Second = (uTime & 0x1f) << 1;
1372 Time.u32Nanosecond = 0;
1373 if (cCentiseconds > 0 && cCentiseconds < 200) /* screw complicated stuff for now. */
1374 {
1375 if (cCentiseconds >= 100)
1376 {
1377 cCentiseconds -= 100;
1378 Time.u8Second++;
1379 }
1380 Time.u32Nanosecond = cCentiseconds * UINT64_C(100000000);
1381 }
1382
1383 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
1384 RTTimeSpecSubNano(pTimeSpec, pVol->offNanoUTC);
1385}
1386
1387
1388/**
1389 * Converts an IPRT timespec to a FAT timestamp.
1390 *
1391 * @returns The centiseconds part.
1392 * @param pVol The volume.
1393 * @param pTimeSpec The IPRT timespec to convert (UTC).
1394 * @param puDate Where to return the date part of the FAT timestamp.
1395 * @param puTime Where to return the time part of the FAT timestamp.
1396 */
1397static uint8_t rtFsFatTimeSpec2FatDateTime(PCRTFSFATVOL pVol, PCRTTIMESPEC pTimeSpec, uint16_t *puDate, uint16_t *puTime)
1398{
1399 RTTIMESPEC TimeSpec = *pTimeSpec;
1400 RTTIME Time;
1401 RTTimeExplode(&Time, RTTimeSpecSubNano(&TimeSpec, pVol->offNanoUTC));
1402
1403 if (puDate)
1404 *puDate = ((RT_MIN(Time.i32Year, 1980) - 1980) << 9)
1405 | (Time.u8Month << 5)
1406 | Time.u8MonthDay;
1407 if (puTime)
1408 *puTime = (Time.u8Hour << 11)
1409 | (Time.u8Minute << 5)
1410 | (Time.u8Second >> 1);
1411 return (Time.u8Second & 1) * 100 + Time.u32Nanosecond / 10000000;
1412
1413}
1414
1415
1416/**
1417 * Gets the current FAT timestamp.
1418 *
1419 * @returns The centiseconds part.
1420 * @param pVol The volume.
1421 * @param puDate Where to return the date part of the FAT timestamp.
1422 * @param puTime Where to return the time part of the FAT timestamp.
1423 */
1424static uint8_t rtFsFatCurrentFatDateTime(PCRTFSFATVOL pVol, uint16_t *puDate, uint16_t *puTime)
1425{
1426 RTTIMESPEC TimeSpec;
1427 return rtFsFatTimeSpec2FatDateTime(pVol, RTTimeNow(&TimeSpec), puDate, puTime);
1428}
1429
1430
1431/**
1432 * Initialization of a RTFSFATOBJ structure from a FAT directory entry.
1433 *
1434 * @note The RTFSFATOBJ::pParentDir and RTFSFATOBJ::Clusters members are
1435 * properly initialized elsewhere.
1436 *
1437 * @param pObj The structure to initialize.
1438 * @param pParentDir The parent directory.
1439 * @param offEntryInDir The offset in the parent directory.
1440 * @param pVol The volume.
1441 */
1442static void rtFsFatObj_InitFromDirEntry(PRTFSFATOBJ pObj, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir, PRTFSFATVOL pVol)
1443{
1444 RTListInit(&pObj->Entry);
1445 pObj->pParentDir = NULL;
1446 pObj->pVol = pVol;
1447 pObj->offEntryInDir = offEntryInDir;
1448 pObj->fAttrib = ((RTFMODE)pDirEntry->fAttrib << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
1449 pObj->cbObject = pDirEntry->cbFile;
1450 pObj->fMaybeDirtyFat = false;
1451 pObj->fMaybeDirtyDirEnt = false;
1452 rtFsFatDateTime2TimeSpec(&pObj->ModificationTime, pDirEntry->uModifyDate, pDirEntry->uModifyTime, 0, pVol);
1453 rtFsFatDateTime2TimeSpec(&pObj->BirthTime, pDirEntry->uBirthDate, pDirEntry->uBirthTime, pDirEntry->uBirthCentiseconds, pVol);
1454 rtFsFatDateTime2TimeSpec(&pObj->AccessTime, pDirEntry->uAccessDate, 0, 0, pVol);
1455}
1456
1457
1458/**
1459 * Dummy initialization of a RTFSFATOBJ structure.
1460 *
1461 * @note The RTFSFATOBJ::pParentDir and RTFSFATOBJ::Clusters members are
1462 * properly initialized elsewhere.
1463 *
1464 * @param pObj The structure to initialize.
1465 * @param cbObject The object size.
1466 * @param fAttrib The attributes.
1467 * @param pVol The volume.
1468 */
1469static void rtFsFatObj_InitDummy(PRTFSFATOBJ pObj, uint32_t cbObject, RTFMODE fAttrib, PRTFSFATVOL pVol)
1470{
1471 RTListInit(&pObj->Entry);
1472 pObj->pParentDir = NULL;
1473 pObj->pVol = pVol;
1474 pObj->offEntryInDir = UINT32_MAX;
1475 pObj->fAttrib = fAttrib;
1476 pObj->cbObject = cbObject;
1477 pObj->fMaybeDirtyFat = false;
1478 pObj->fMaybeDirtyDirEnt = false;
1479 RTTimeSpecSetDosSeconds(&pObj->AccessTime, 0);
1480 RTTimeSpecSetDosSeconds(&pObj->ModificationTime, 0);
1481 RTTimeSpecSetDosSeconds(&pObj->BirthTime, 0);
1482}
1483
1484
1485/**
1486 * Flushes FAT object meta data.
1487 *
1488 * @returns IPRT status code
1489 * @param pObj The common object structure.
1490 */
1491static int rtFsFatObj_FlushMetaData(PRTFSFATOBJ pObj)
1492{
1493 int rc = VINF_SUCCESS;
1494 if (pObj->fMaybeDirtyFat)
1495 {
1496 rc = rtFsFatClusterMap_Flush(pObj->pVol);
1497 if (RT_SUCCESS(rc))
1498 pObj->fMaybeDirtyFat = false;
1499 }
1500 if (pObj->fMaybeDirtyDirEnt)
1501 {
1502 int rc2 = rtFsFatDir_Flush(pObj->pParentDir);
1503 if (RT_SUCCESS(rc2))
1504 pObj->fMaybeDirtyDirEnt = false;
1505 else if (RT_SUCCESS(rc))
1506 rc = rc2;
1507 }
1508 return rc;
1509}
1510
1511
1512/**
1513 * Worker for rtFsFatFile_Close and rtFsFatDir_Close that does common work.
1514 *
1515 * @returns IPRT status code.
1516 * @param pObj The common object structure.
1517 */
1518static int rtFsFatObj_Close(PRTFSFATOBJ pObj)
1519{
1520 int rc = rtFsFatObj_FlushMetaData(pObj);
1521 if (pObj->pParentDir)
1522 rtFsFatDir_RemoveOpenChild(pObj->pParentDir, pObj);
1523 rtFsFatChain_Delete(&pObj->Clusters);
1524 return rc;
1525}
1526
1527
1528/**
1529 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1530 */
1531static DECLCALLBACK(int) rtFsFatFile_Close(void *pvThis)
1532{
1533 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1534 return rtFsFatObj_Close(&pThis->Core);
1535}
1536
1537
1538/**
1539 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1540 */
1541static DECLCALLBACK(int) rtFsFatObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1542{
1543 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1544
1545 pObjInfo->cbObject = pThis->Core.cbObject;
1546 pObjInfo->cbAllocated = pThis->Core.Clusters.cClusters * pThis->Core.pVol->cbCluster;
1547 pObjInfo->AccessTime = pThis->Core.AccessTime;
1548 pObjInfo->ModificationTime = pThis->Core.ModificationTime;
1549 pObjInfo->ChangeTime = pThis->Core.ModificationTime;
1550 pObjInfo->BirthTime = pThis->Core.BirthTime;
1551 pObjInfo->Attr.fMode = pThis->Core.fAttrib;
1552 pObjInfo->Attr.enmAdditional = enmAddAttr;
1553
1554 switch (enmAddAttr)
1555 {
1556 case RTFSOBJATTRADD_NOTHING: /* fall thru */
1557 case RTFSOBJATTRADD_UNIX:
1558 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
1559 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
1560 pObjInfo->Attr.u.Unix.cHardlinks = 1;
1561 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
1562 pObjInfo->Attr.u.Unix.INodeId = 0; /* Could probably use the directory entry offset. */
1563 pObjInfo->Attr.u.Unix.fFlags = 0;
1564 pObjInfo->Attr.u.Unix.GenerationId = 0;
1565 pObjInfo->Attr.u.Unix.Device = 0;
1566 break;
1567 case RTFSOBJATTRADD_UNIX_OWNER:
1568 pObjInfo->Attr.u.UnixOwner.uid = 0;
1569 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
1570 break;
1571 case RTFSOBJATTRADD_UNIX_GROUP:
1572 pObjInfo->Attr.u.UnixGroup.gid = 0;
1573 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
1574 break;
1575 case RTFSOBJATTRADD_EASIZE:
1576 pObjInfo->Attr.u.EASize.cb = 0;
1577 break;
1578 default:
1579 return VERR_INVALID_PARAMETER;
1580 }
1581 return VINF_SUCCESS;
1582}
1583
1584
1585/**
1586 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1587 */
1588static DECLCALLBACK(int) rtFsFatFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1589{
1590 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1591 AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
1592 RT_NOREF(fBlocking);
1593
1594 /*
1595 * Check for EOF.
1596 */
1597 if (off == -1)
1598 off = pThis->offFile;
1599 if ((uint64_t)off >= pThis->Core.cbObject)
1600 {
1601 if (pcbRead)
1602 {
1603 *pcbRead = 0;
1604 return VINF_EOF;
1605 }
1606 return VERR_EOF;
1607 }
1608
1609 /*
1610 * Do the reading cluster by cluster.
1611 */
1612 int rc = VINF_SUCCESS;
1613 uint32_t cbFileLeft = pThis->Core.cbObject - (uint32_t)off;
1614 uint32_t cbRead = 0;
1615 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
1616 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
1617 while (cbLeft > 0)
1618 {
1619 if (cbFileLeft > 0)
1620 {
1621 uint64_t offDisk = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, (uint32_t)off, pThis->Core.pVol);
1622 if (offDisk != UINT64_MAX)
1623 {
1624 uint32_t cbToRead = pThis->Core.Clusters.cbCluster - ((uint32_t)off & (pThis->Core.Clusters.cbCluster - 1));
1625 if (cbToRead > cbLeft)
1626 cbToRead = (uint32_t)cbLeft;
1627 if (cbToRead > cbFileLeft)
1628 cbToRead = cbFileLeft;
1629 rc = RTVfsFileReadAt(pThis->Core.pVol->hVfsBacking, offDisk, pbDst, cbToRead, NULL);
1630 if (RT_SUCCESS(rc))
1631 {
1632 off += cbToRead;
1633 pbDst += cbToRead;
1634 cbRead += cbToRead;
1635 cbFileLeft -= cbToRead;
1636 cbLeft -= cbToRead;
1637 continue;
1638 }
1639 }
1640 else
1641 rc = VERR_VFS_BOGUS_OFFSET;
1642 }
1643 else
1644 rc = pcbRead ? VINF_EOF : VERR_EOF;
1645 break;
1646 }
1647
1648 /* Update the offset and return. */
1649 pThis->offFile = off;
1650 if (pcbRead)
1651 *pcbRead = cbRead;
1652 return VINF_SUCCESS;
1653}
1654
1655
1656/**
1657 * Changes the size of a file or directory FAT object.
1658 *
1659 * @returns IPRT status code
1660 * @param pObj The common object.
1661 * @param cbFile The new file size.
1662 */
1663static int rtFsFatObj_SetSize(PRTFSFATOBJ pObj, uint32_t cbFile)
1664{
1665 AssertReturn( ((pObj->cbObject + pObj->Clusters.cbCluster - 1) >> pObj->Clusters.cClusterByteShift)
1666 == pObj->Clusters.cClusters, VERR_INTERNAL_ERROR_3);
1667
1668 /*
1669 * Do nothing if the size didn't change.
1670 */
1671 if (pObj->cbObject == cbFile)
1672 return VINF_SUCCESS;
1673
1674 /*
1675 * Do we need to allocate or free clusters?
1676 */
1677 int rc = VINF_SUCCESS;
1678 uint32_t const cClustersNew = (cbFile + pObj->Clusters.cbCluster - 1) >> pObj->Clusters.cClusterByteShift;
1679 AssertReturn(pObj->pParentDir, VERR_INTERNAL_ERROR_2);
1680 if (pObj->Clusters.cClusters == cClustersNew)
1681 { /* likely when writing small bits at a time. */ }
1682 else if (pObj->Clusters.cClusters < cClustersNew)
1683 {
1684 /* Allocate and append new clusters. */
1685 do
1686 {
1687 uint32_t idxCluster;
1688 rc = rtFsFatClusterMap_AllocateCluster(pObj->pVol, rtFsFatChain_GetLastCluster(&pObj->Clusters), &idxCluster);
1689 if (RT_SUCCESS(rc))
1690 rc = rtFsFatChain_Append(&pObj->Clusters, idxCluster);
1691 } while (pObj->Clusters.cClusters < cClustersNew && RT_SUCCESS(rc));
1692 pObj->fMaybeDirtyFat = true;
1693 }
1694 else
1695 {
1696 /* Free clusters we don't need any more. */
1697 if (cClustersNew > 0)
1698 rc = rtFsFatClusterMap_SetEndOfChain(pObj->pVol, rtFsFatChain_GetClusterByIndex(&pObj->Clusters, cClustersNew - 1));
1699 if (RT_SUCCESS(rc))
1700 {
1701 uint32_t iClusterToFree = cClustersNew;
1702 while (iClusterToFree < pObj->Clusters.cClusters && RT_SUCCESS(rc))
1703 {
1704 rc = rtFsFatClusterMap_FreeCluster(pObj->pVol, rtFsFatChain_GetClusterByIndex(&pObj->Clusters, iClusterToFree));
1705 iClusterToFree++;
1706 }
1707
1708 rtFsFatChain_Shrink(&pObj->Clusters, cClustersNew);
1709 }
1710 pObj->fMaybeDirtyFat = true;
1711 }
1712 if (RT_SUCCESS(rc))
1713 {
1714 /*
1715 * Update the object size, since we've got the right number of clusters backing it now.
1716 */
1717 pObj->cbObject = cbFile;
1718
1719 /*
1720 * Update the directory entry.
1721 */
1722 uint32_t uWriteLock;
1723 PFATDIRENTRY pDirEntry;
1724 rc = rtFsFatDir_GetEntryForUpdate(pObj->pParentDir, pObj->offEntryInDir, &pDirEntry, &uWriteLock);
1725 if (RT_SUCCESS(rc))
1726 {
1727 pDirEntry->cbFile = cbFile;
1728 uint32_t idxFirstCluster;
1729 if (cClustersNew == 0)
1730 idxFirstCluster = 0; /** @todo figure out if setting the cluster to 0 is the right way to deal with empty files... */
1731 else
1732 idxFirstCluster = rtFsFatChain_GetFirstCluster(&pObj->Clusters);
1733 pDirEntry->idxCluster = (uint16_t)idxFirstCluster;
1734 if (pObj->pVol->enmFatType >= RTFSFATTYPE_FAT32)
1735 pDirEntry->u.idxClusterHigh = (uint16_t)(idxFirstCluster >> 16);
1736
1737 rc = rtFsFatDir_PutEntryAfterUpdate(pObj->pParentDir, pDirEntry, uWriteLock);
1738 pObj->fMaybeDirtyDirEnt = true;
1739 }
1740 }
1741 return rc;
1742}
1743
1744
1745/**
1746 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1747 */
1748static DECLCALLBACK(int) rtFsFatFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1749{
1750 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1751 PRTFSFATVOL pVol = pThis->Core.pVol;
1752 AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
1753 RT_NOREF(fBlocking);
1754
1755 if (pVol->fReadOnly)
1756 return VERR_WRITE_PROTECT;
1757
1758 if (off == -1)
1759 off = pThis->offFile;
1760
1761 /*
1762 * Do the reading cluster by cluster.
1763 */
1764 int rc = VINF_SUCCESS;
1765 uint32_t cbWritten = 0;
1766 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
1767 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
1768 while (cbLeft > 0)
1769 {
1770 /* Figure out how much we can write. Checking for max file size and such. */
1771 uint32_t cbToWrite = pThis->Core.Clusters.cbCluster - ((uint32_t)off & (pThis->Core.Clusters.cbCluster - 1));
1772 if (cbToWrite > cbLeft)
1773 cbToWrite = (uint32_t)cbLeft;
1774 uint64_t offNew = (uint64_t)off + cbToWrite;
1775 if (offNew < _4G)
1776 { /*likely*/ }
1777 else if ((uint64_t)off < _4G - 1U)
1778 cbToWrite = _4G - 1U - off;
1779 else
1780 {
1781 rc = VERR_FILE_TOO_BIG;
1782 break;
1783 }
1784
1785 /* Grow the file? */
1786 if ((uint32_t)offNew > pThis->Core.cbObject)
1787 {
1788 rc = rtFsFatObj_SetSize(&pThis->Core, (uint32_t)offNew);
1789 if (RT_SUCCESS(rc))
1790 { /* likely */}
1791 else
1792 break;
1793 }
1794
1795 /* Figure the disk offset. */
1796 uint64_t offDisk = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, (uint32_t)off, pVol);
1797 if (offDisk != UINT64_MAX)
1798 {
1799 rc = RTVfsFileWriteAt(pVol->hVfsBacking, offDisk, pbSrc, cbToWrite, NULL);
1800 if (RT_SUCCESS(rc))
1801 {
1802 off += cbToWrite;
1803 pbSrc += cbToWrite;
1804 cbWritten += cbToWrite;
1805 cbLeft -= cbToWrite;
1806 }
1807 else
1808 break;
1809 }
1810 else
1811 {
1812 rc = VERR_VFS_BOGUS_OFFSET;
1813 break;
1814 }
1815 }
1816
1817 /* Update the offset and return. */
1818 pThis->offFile = off;
1819 if (pcbWritten)
1820 *pcbWritten = cbWritten;
1821 return VINF_SUCCESS;
1822}
1823
1824
1825/**
1826 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1827 */
1828static DECLCALLBACK(int) rtFsFatFile_Flush(void *pvThis)
1829{
1830 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1831 int rc1 = rtFsFatObj_FlushMetaData(&pThis->Core);
1832 int rc2 = RTVfsFileFlush(pThis->Core.pVol->hVfsBacking);
1833 return RT_FAILURE(rc1) ? rc1 : rc2;
1834}
1835
1836
1837/**
1838 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
1839 */
1840static DECLCALLBACK(int) rtFsFatFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
1841 uint32_t *pfRetEvents)
1842{
1843 NOREF(pvThis);
1844 int rc;
1845 if (fEvents != RTPOLL_EVT_ERROR)
1846 {
1847 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
1848 rc = VINF_SUCCESS;
1849 }
1850 else if (fIntr)
1851 rc = RTThreadSleep(cMillies);
1852 else
1853 {
1854 uint64_t uMsStart = RTTimeMilliTS();
1855 do
1856 rc = RTThreadSleep(cMillies);
1857 while ( rc == VERR_INTERRUPTED
1858 && !fIntr
1859 && RTTimeMilliTS() - uMsStart < cMillies);
1860 if (rc == VERR_INTERRUPTED)
1861 rc = VERR_TIMEOUT;
1862 }
1863 return rc;
1864}
1865
1866
1867/**
1868 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1869 */
1870static DECLCALLBACK(int) rtFsFatFile_Tell(void *pvThis, PRTFOFF poffActual)
1871{
1872 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1873 *poffActual = pThis->offFile;
1874 return VINF_SUCCESS;
1875}
1876
1877
1878/**
1879 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1880 */
1881static DECLCALLBACK(int) rtFsFatObj_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1882{
1883#if 0
1884 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1885 if (fMask != ~RTFS_TYPE_MASK)
1886 {
1887 fMode |= ~fMask & ObjInfo.Attr.fMode;
1888 }
1889#else
1890 RT_NOREF(pvThis, fMode, fMask);
1891 return VERR_NOT_IMPLEMENTED;
1892#endif
1893}
1894
1895
1896/**
1897 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1898 */
1899static DECLCALLBACK(int) rtFsFatObj_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1900 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1901{
1902#if 0
1903 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1904#else
1905 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1906 return VERR_NOT_IMPLEMENTED;
1907#endif
1908}
1909
1910
1911/**
1912 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1913 */
1914static DECLCALLBACK(int) rtFsFatObj_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1915{
1916 RT_NOREF(pvThis, uid, gid);
1917 return VERR_NOT_SUPPORTED;
1918}
1919
1920
1921/**
1922 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1923 */
1924static DECLCALLBACK(int) rtFsFatFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1925{
1926 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1927 RTFOFF offNew;
1928 switch (uMethod)
1929 {
1930 case RTFILE_SEEK_BEGIN:
1931 offNew = offSeek;
1932 break;
1933 case RTFILE_SEEK_END:
1934 offNew = (RTFOFF)pThis->Core.cbObject + offSeek;
1935 break;
1936 case RTFILE_SEEK_CURRENT:
1937 offNew = (RTFOFF)pThis->offFile + offSeek;
1938 break;
1939 default:
1940 return VERR_INVALID_PARAMETER;
1941 }
1942 if (offNew >= 0)
1943 {
1944 if (offNew <= _4G)
1945 {
1946 pThis->offFile = offNew;
1947 *poffActual = offNew;
1948 return VINF_SUCCESS;
1949 }
1950 return VERR_OUT_OF_RANGE;
1951 }
1952 return VERR_NEGATIVE_SEEK;
1953}
1954
1955
1956/**
1957 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1958 */
1959static DECLCALLBACK(int) rtFsFatFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1960{
1961 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
1962 *pcbFile = pThis->Core.cbObject;
1963 return VINF_SUCCESS;
1964}
1965
1966
1967/**
1968 * FAT file operations.
1969 */
1970DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsFatFileOps =
1971{
1972 { /* Stream */
1973 { /* Obj */
1974 RTVFSOBJOPS_VERSION,
1975 RTVFSOBJTYPE_FILE,
1976 "FatFile",
1977 rtFsFatFile_Close,
1978 rtFsFatObj_QueryInfo,
1979 RTVFSOBJOPS_VERSION
1980 },
1981 RTVFSIOSTREAMOPS_VERSION,
1982 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1983 rtFsFatFile_Read,
1984 rtFsFatFile_Write,
1985 rtFsFatFile_Flush,
1986 rtFsFatFile_PollOne,
1987 rtFsFatFile_Tell,
1988 NULL /*pfnSkip*/,
1989 NULL /*pfnZeroFill*/,
1990 RTVFSIOSTREAMOPS_VERSION,
1991 },
1992 RTVFSFILEOPS_VERSION,
1993 0,
1994 { /* ObjSet */
1995 RTVFSOBJSETOPS_VERSION,
1996 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
1997 rtFsFatObj_SetMode,
1998 rtFsFatObj_SetTimes,
1999 rtFsFatObj_SetOwner,
2000 RTVFSOBJSETOPS_VERSION
2001 },
2002 rtFsFatFile_Seek,
2003 rtFsFatFile_QuerySize,
2004 RTVFSFILEOPS_VERSION
2005};
2006
2007
2008/**
2009 * Instantiates a new directory.
2010 *
2011 * @returns IPRT status code.
2012 * @param pThis The FAT volume instance.
2013 * @param pParentDir The parent directory.
2014 * @param pDirEntry The parent directory entry.
2015 * @param offEntryInDir The byte offset of the directory entry in the parent
2016 * directory.
2017 * @param fOpen RTFILE_O_XXX flags.
2018 * @param phVfsFile Where to return the file handle.
2019 */
2020static int rtFsFatFile_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
2021 uint64_t fOpen, PRTVFSFILE phVfsFile)
2022{
2023 AssertPtr(pParentDir);
2024 Assert(!(offEntryInDir & (sizeof(FATDIRENTRY) - 1)));
2025
2026 PRTFSFATFILE pNewFile;
2027 int rc = RTVfsNewFile(&g_rtFsFatFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2028 phVfsFile, (void **)&pNewFile);
2029 if (RT_SUCCESS(rc))
2030 {
2031 /*
2032 * Initialize it all so rtFsFatFile_Close doesn't trip up in anyway.
2033 */
2034 rtFsFatObj_InitFromDirEntry(&pNewFile->Core, pDirEntry, offEntryInDir, pThis);
2035 pNewFile->offFile = 0;
2036 rc = rtFsFatClusterMap_ReadClusterChain(pThis, RTFSFAT_GET_CLUSTER(pDirEntry, pThis), &pNewFile->Core.Clusters);
2037 if (RT_SUCCESS(rc))
2038 {
2039 /*
2040 * Link into parent directory so we can use it to update
2041 * our directory entry.
2042 */
2043 rtFsFatDir_AddOpenChild(pParentDir, &pNewFile->Core);
2044
2045 /*
2046 * Should we truncate the file or anything of that sort?
2047 */
2048 if ( (fOpen & RTFILE_O_TRUNCATE)
2049 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
2050 rc = rtFsFatObj_SetSize(&pNewFile->Core, 0);
2051 if (RT_SUCCESS(rc))
2052 return VINF_SUCCESS;
2053 }
2054
2055 RTVfsFileRelease(*phVfsFile);
2056 }
2057 *phVfsFile = NIL_RTVFSFILE;
2058 return rc;
2059}
2060
2061
2062
2063
2064/**
2065 * Flush directory changes when having a fully buffered directory.
2066 *
2067 * @returns IPRT status code
2068 * @param pThis The directory.
2069 */
2070static int rtFsFatDir_FlushFullyBuffered(PRTFSFATDIR pThis)
2071{
2072 Assert(pThis->fFullyBuffered);
2073 uint32_t const cbSector = pThis->Core.pVol->cbSector;
2074 RTVFSFILE const hVfsBacking = pThis->Core.pVol->hVfsBacking;
2075 int rc = VINF_SUCCESS;
2076 for (uint32_t i = 0; i < pThis->u.Full.cSectors; i++)
2077 if (pThis->u.Full.pbDirtySectors[i])
2078 {
2079 int rc2 = RTVfsFileWriteAt(hVfsBacking, pThis->offEntriesOnDisk + i * cbSector,
2080 (uint8_t *)pThis->paEntries + i * cbSector, cbSector, NULL);
2081 if (RT_SUCCESS(rc2))
2082 pThis->u.Full.pbDirtySectors[i] = false;
2083 else if (RT_SUCCESS(rc))
2084 rc = rc2;
2085 }
2086 return rc;
2087}
2088
2089
2090/**
2091 * Flush directory changes when using simple buffering.
2092 *
2093 * @returns IPRT status code
2094 * @param pThis The directory.
2095 */
2096static int rtFsFatDir_FlushSimple(PRTFSFATDIR pThis)
2097{
2098 Assert(!pThis->fFullyBuffered);
2099 int rc;
2100 if ( !pThis->u.Simple.fDirty
2101 || pThis->offEntriesOnDisk != UINT64_MAX)
2102 rc = VINF_SUCCESS;
2103 else
2104 {
2105 Assert(pThis->u.Simple.offInDir != UINT32_MAX);
2106 rc = RTVfsFileWriteAt(pThis->Core.pVol->hVfsBacking, pThis->offEntriesOnDisk,
2107 pThis->paEntries, pThis->Core.pVol->cbSector, NULL);
2108 if (RT_SUCCESS(rc))
2109 pThis->u.Simple.fDirty = false;
2110 }
2111 return rc;
2112}
2113
2114
2115/**
2116 * Flush directory changes.
2117 *
2118 * @returns IPRT status code
2119 * @param pThis The directory.
2120 */
2121static int rtFsFatDir_Flush(PRTFSFATDIR pThis)
2122{
2123 if (pThis->fFullyBuffered)
2124 return rtFsFatDir_FlushFullyBuffered(pThis);
2125 return rtFsFatDir_FlushSimple(pThis);
2126}
2127
2128
2129/**
2130 * Gets one or more entires at @a offEntryInDir.
2131 *
2132 * Common worker for rtFsFatDir_GetEntriesAt and rtFsFatDir_GetEntryForUpdate
2133 *
2134 * @returns IPRT status code.
2135 * @param pThis The directory.
2136 * @param offEntryInDir The directory offset in bytes.
2137 * @param fForUpdate Whether it's for updating.
2138 * @param ppaEntries Where to return pointer to the entry at
2139 * @a offEntryInDir.
2140 * @param pcEntries Where to return the number of entries
2141 * @a *ppaEntries points to.
2142 * @param puBufferReadLock Where to return the buffer read lock handle.
2143 * Call rtFsFatDir_ReleaseBufferAfterReading when
2144 * done.
2145 */
2146static int rtFsFatDir_GetEntriesAtCommon(PRTFSFATDIR pThis, uint32_t offEntryInDir, bool fForUpdate,
2147 PFATDIRENTRYUNION *ppaEntries, uint32_t *pcEntries, uint32_t *puLock)
2148{
2149 *puLock = UINT32_MAX;
2150
2151 int rc;
2152 Assert(RT_ALIGN_32(offEntryInDir, sizeof(FATDIRENTRY)) == offEntryInDir);
2153 Assert(pThis->Core.cbObject / sizeof(FATDIRENTRY) == pThis->cEntries);
2154 uint32_t const idxEntryInDir = offEntryInDir / sizeof(FATDIRENTRY);
2155 if (idxEntryInDir < pThis->cEntries)
2156 {
2157 if (pThis->fFullyBuffered)
2158 {
2159 /*
2160 * Fully buffered: Return pointer to all the entires starting at offEntryInDir.
2161 */
2162 *ppaEntries = &pThis->paEntries[idxEntryInDir];
2163 *pcEntries = pThis->cEntries - idxEntryInDir;
2164 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001);
2165 rc = VINF_SUCCESS;
2166 }
2167 else
2168 {
2169 /*
2170 * Simple buffering: If hit, return the number of entries.
2171 */
2172 PRTFSFATVOL pVol = pThis->Core.pVol;
2173 uint32_t off = offEntryInDir - pThis->u.Simple.offInDir;
2174 if (off < pVol->cbSector)
2175 {
2176 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)];
2177 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY);
2178 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001);
2179 rc = VINF_SUCCESS;
2180 }
2181 else
2182 {
2183 /*
2184 * Simple buffering: Miss.
2185 * Flush dirty. Read in new sector. Return entries in sector starting
2186 * at offEntryInDir.
2187 */
2188 if (!pThis->u.Simple.fDirty)
2189 rc = VINF_SUCCESS;
2190 else
2191 rc = rtFsFatDir_FlushSimple(pThis);
2192 if (RT_SUCCESS(rc))
2193 {
2194 off = offEntryInDir & (pVol->cbSector - 1);
2195 pThis->u.Simple.offInDir = (offEntryInDir & ~(pVol->cbSector - 1));
2196 pThis->offEntriesOnDisk = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, pThis->u.Simple.offInDir,
2197 pThis->Core.pVol);
2198 rc = RTVfsFileReadAt(pThis->Core.pVol->hVfsBacking, pThis->offEntriesOnDisk,
2199 pThis->paEntries, pVol->cbSector, NULL);
2200 if (RT_SUCCESS(rc))
2201 {
2202 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)];
2203 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY);
2204 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001);
2205 rc = VINF_SUCCESS;
2206 }
2207 else
2208 {
2209 pThis->u.Simple.offInDir = UINT32_MAX;
2210 pThis->offEntriesOnDisk = UINT64_MAX;
2211 }
2212 }
2213 }
2214 }
2215 }
2216 else
2217 rc = VERR_FILE_NOT_FOUND;
2218 return rc;
2219}
2220
2221
2222/**
2223 * Puts back a directory entry after updating it, releasing the write lock and
2224 * marking it dirty.
2225 *
2226 * @returns IPRT status code
2227 * @param pThis The directory.
2228 * @param pDirEntry The directory entry.
2229 * @param uWriteLock The write lock.
2230 */
2231static int rtFsFatDir_PutEntryAfterUpdate(PRTFSFATDIR pThis, PFATDIRENTRY pDirEntry, uint32_t uWriteLock)
2232{
2233 Assert(uWriteLock == UINT32_C(0x80000001));
2234 RT_NOREF(uWriteLock);
2235 if (pThis->fFullyBuffered)
2236 {
2237 uint32_t idxSector = ((uintptr_t)pDirEntry - (uintptr_t)pThis->paEntries) / pThis->Core.pVol->cbSector;
2238 pThis->u.Full.pbDirtySectors[idxSector] = true;
2239 }
2240 else
2241 pThis->u.Simple.fDirty = true;
2242 return VINF_SUCCESS;
2243}
2244
2245
2246/**
2247 * Gets the pointer to the given directory entry for the purpose of updating it.
2248 *
2249 * Call rtFsFatDir_PutEntryAfterUpdate afterwards.
2250 *
2251 * @returns IPRT status code.
2252 * @param pThis The directory.
2253 * @param offEntryInDir The byte offset of the directory entry, within the
2254 * directory.
2255 * @param ppDirEntry Where to return the pointer to the directory entry.
2256 * @param puWriteLock Where to return the write lock.
2257 */
2258static int rtFsFatDir_GetEntryForUpdate(PRTFSFATDIR pThis, uint32_t offEntryInDir, PFATDIRENTRY *ppDirEntry,
2259 uint32_t *puWriteLock)
2260{
2261 uint32_t cEntriesIgn;
2262 return rtFsFatDir_GetEntriesAtCommon(pThis, offEntryInDir, true /*fForUpdate*/, (PFATDIRENTRYUNION *)ppDirEntry,
2263 &cEntriesIgn, puWriteLock);
2264}
2265
2266
2267/**
2268 * Release a directory buffer after done reading from it.
2269 *
2270 * This is currently just a placeholder.
2271 *
2272 * @param pThis The directory.
2273 * @param uBufferReadLock The buffer lock.
2274 */
2275static void rtFsFatDir_ReleaseBufferAfterReading(PRTFSFATDIR pThis, uint32_t uBufferReadLock)
2276{
2277 RT_NOREF(pThis, uBufferReadLock);
2278 Assert(uBufferReadLock == 1);
2279}
2280
2281
2282/**
2283 * Gets one or more entires at @a offEntryInDir.
2284 *
2285 * @returns IPRT status code.
2286 * @param pThis The directory.
2287 * @param offEntryInDir The directory offset in bytes.
2288 * @param ppaEntries Where to return pointer to the entry at
2289 * @a offEntryInDir.
2290 * @param pcEntries Where to return the number of entries
2291 * @a *ppaEntries points to.
2292 * @param puBufferReadLock Where to return the buffer read lock handle.
2293 * Call rtFsFatDir_ReleaseBufferAfterReading when
2294 * done.
2295 */
2296static int rtFsFatDir_GetEntriesAt(PRTFSFATDIR pThis, uint32_t offEntryInDir,
2297 PCFATDIRENTRYUNION *ppaEntries, uint32_t *pcEntries, uint32_t *puBufferReadLock)
2298{
2299 return rtFsFatDir_GetEntriesAtCommon(pThis, offEntryInDir, false /*fForUpdate*/, (PFATDIRENTRYUNION *)ppaEntries,
2300 pcEntries, puBufferReadLock);
2301}
2302
2303
2304/**
2305 * Translates a unicode codepoint to an uppercased CP437 index.
2306 *
2307 * @returns CP437 index if valie, UINT16_MAX if not.
2308 * @param uc The codepoint to convert.
2309 */
2310static uint16_t rtFsFatUnicodeCodepointToUpperCodepage(RTUNICP uc)
2311{
2312 /*
2313 * The first 128 chars have 1:1 translation for valid FAT chars.
2314 */
2315 if (uc < 128)
2316 {
2317 if (g_awchFatCp437Chars[uc] == uc)
2318 return (uint16_t)uc;
2319 if (RT_C_IS_LOWER(uc))
2320 return uc - 0x20;
2321 return UINT16_MAX;
2322 }
2323
2324 /*
2325 * Try for uppercased, settle for lower case if no upper case variant in the table.
2326 * This is really expensive, btw.
2327 */
2328 RTUNICP ucUpper = RTUniCpToUpper(uc);
2329 for (unsigned i = 128; i < 256; i++)
2330 if (g_awchFatCp437Chars[i] == ucUpper)
2331 return i;
2332 if (ucUpper != uc)
2333 for (unsigned i = 128; i < 256; i++)
2334 if (g_awchFatCp437Chars[i] == uc)
2335 return i;
2336 return UINT16_MAX;
2337}
2338
2339
2340/**
2341 * Convert filename string to 8-dot-3 format, doing necessary ASCII uppercasing
2342 * and such.
2343 *
2344 * @returns true if 8.3 formattable name, false if not.
2345 * @param pszName8Dot3 Where to return the 8-dot-3 name when returning
2346 * @c true. Filled with zero on false. 12+1 bytes.
2347 * @param pszName The filename to convert.
2348 */
2349static bool rtFsFatDir_StringTo8Dot3(char *pszName8Dot3, const char *pszName)
2350{
2351 /*
2352 * Don't try convert names with more than 12 unicode chars in them.
2353 */
2354 size_t const cucName = RTStrUniLen(pszName);
2355 if (cucName <= 12 && cucName > 0)
2356 {
2357 /*
2358 * Recode the input string as CP437, uppercasing it, validating the
2359 * name, formatting it as a FAT directory entry string.
2360 */
2361 size_t offDst = 0;
2362 bool fExt = false;
2363 for (;;)
2364 {
2365 RTUNICP uc;
2366 int rc = RTStrGetCpEx(&pszName, &uc);
2367 if (RT_SUCCESS(rc))
2368 {
2369 if (uc)
2370 {
2371 if (offDst < 8+3)
2372 {
2373 uint16_t idxCp = rtFsFatUnicodeCodepointToUpperCodepage(uc);
2374 if (idxCp != UINT16_MAX)
2375 {
2376 pszName8Dot3[offDst++] = (char)idxCp;
2377 Assert(uc != '.');
2378 continue;
2379 }
2380
2381 /* Maybe the dot? */
2382 if ( uc == '.'
2383 && !fExt
2384 && offDst <= 8)
2385 {
2386 fExt = true;
2387 while (offDst < 8)
2388 pszName8Dot3[offDst++] = ' ';
2389 continue;
2390 }
2391 }
2392 }
2393 /* String terminator: Check length, pad and convert 0xe5. */
2394 else if (offDst <= (size_t)(fExt ? 8 + 3 : 8))
2395 {
2396 while (offDst < 8 + 3)
2397 pszName8Dot3[offDst++] = ' ';
2398 Assert(offDst == 8 + 3);
2399 pszName8Dot3[offDst] = '\0';
2400
2401 if ((uint8_t)pszName8Dot3[0] == FATDIRENTRY_CH0_DELETED)
2402 pszName8Dot3[0] = FATDIRENTRY_CH0_ESC_E5;
2403 return true;
2404 }
2405 }
2406 /* invalid */
2407 break;
2408 }
2409 }
2410 memset(&pszName8Dot3[0], 0, 12+1);
2411 return false;
2412}
2413
2414
2415/**
2416 * Calculates the checksum of a directory entry.
2417 * @returns Checksum.
2418 * @param pDirEntry The directory entry to checksum.
2419 */
2420static uint8_t rtFsFatDir_CalcChecksum(PCFATDIRENTRY pDirEntry)
2421{
2422 uint8_t bChecksum = pDirEntry->achName[0];
2423 for (uint8_t off = 1; off < RT_ELEMENTS(pDirEntry->achName); off++)
2424 {
2425 bChecksum = RTFSFAT_ROT_R1_U8(bChecksum);
2426 bChecksum += pDirEntry->achName[off];
2427 }
2428 return bChecksum;
2429}
2430
2431
2432/**
2433 * Locates a directory entry in a directory.
2434 *
2435 * @returns IPRT status code.
2436 * @param pThis The directory to search.
2437 * @param pszEntry The entry to look for.
2438 * @param poffEntryInDir Where to return the offset of the directory
2439 * entry.
2440 * @param pfLong Where to return long name indicator.
2441 * @param pDirEntry Where to return a copy of the directory entry.
2442 */
2443static int rtFsFatDir_FindEntry(PRTFSFATDIR pThis, const char *pszEntry, uint32_t *poffEntryInDir, bool *pfLong,
2444 PFATDIRENTRY pDirEntry)
2445{
2446 /* Set return values. */
2447 *pfLong = false;
2448 *poffEntryInDir = UINT32_MAX;
2449
2450 /*
2451 * Turn pszEntry into a 8.3 filename, if possible.
2452 */
2453 char szName8Dot3[12+1];
2454 bool fIs8Dot3Name = rtFsFatDir_StringTo8Dot3(szName8Dot3, pszEntry);
2455
2456 /*
2457 * Scan the directory buffer by buffer.
2458 */
2459 RTUTF16 wszName[260+1];
2460 uint8_t bChecksum = UINT8_MAX;
2461 uint8_t idNextSlot = UINT8_MAX;
2462 size_t cwcName = 0;
2463 uint32_t offEntryInDir = 0;
2464 uint32_t const cbDir = pThis->Core.cbObject;
2465 Assert(RT_ALIGN_32(cbDir, sizeof(*pDirEntry)) == cbDir);
2466 AssertCompile(FATDIRNAMESLOT_MAX_SLOTS * FATDIRNAMESLOT_CHARS_PER_SLOT < RT_ELEMENTS(wszName));
2467 wszName[260] = '\0';
2468
2469 while (offEntryInDir < cbDir)
2470 {
2471 /* Get chunk of entries starting at offEntryInDir. */
2472 uint32_t uBufferLock = UINT32_MAX;
2473 uint32_t cEntries = 0;
2474 PCFATDIRENTRYUNION paEntries = NULL;
2475 int rc = rtFsFatDir_GetEntriesAt(pThis, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
2476 if (RT_FAILURE(rc))
2477 return rc;
2478
2479 /*
2480 * Now work thru each of the entries.
2481 */
2482 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
2483 {
2484 switch ((uint8_t)paEntries[iEntry].Entry.achName[0])
2485 {
2486 default:
2487 break;
2488 case FATDIRENTRY_CH0_DELETED:
2489 cwcName = 0;
2490 continue;
2491 case FATDIRENTRY_CH0_END_OF_DIR:
2492 if (pThis->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
2493 {
2494 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2495 return VERR_FILE_NOT_FOUND;
2496 }
2497 cwcName = 0;
2498 break; /* Technically a valid entry before DOS 2.0, or so some claim. */
2499 }
2500
2501 /*
2502 * Check for long filename slot.
2503 */
2504 if ( paEntries[iEntry].Slot.fAttrib == FAT_ATTR_NAME_SLOT
2505 && paEntries[iEntry].Slot.idxZero == 0
2506 && paEntries[iEntry].Slot.fZero == 0
2507 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) <= FATDIRNAMESLOT_HIGHEST_SLOT_ID
2508 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) != 0)
2509 {
2510 /* New slot? */
2511 if (paEntries[iEntry].Slot.idSlot & FATDIRNAMESLOT_FIRST_SLOT_FLAG)
2512 {
2513 idNextSlot = paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG;
2514 bChecksum = paEntries[iEntry].Slot.bChecksum;
2515 cwcName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
2516 wszName[cwcName] = '\0';
2517 }
2518 /* Is valid next entry? */
2519 else if ( paEntries[iEntry].Slot.idSlot == idNextSlot
2520 && paEntries[iEntry].Slot.bChecksum == bChecksum)
2521 { /* likely */ }
2522 else
2523 cwcName = 0;
2524 if (cwcName)
2525 {
2526 idNextSlot--;
2527 size_t offName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
2528 memcpy(&wszName[offName], paEntries[iEntry].Slot.awcName0, sizeof(paEntries[iEntry].Slot.awcName0));
2529 memcpy(&wszName[offName + 5], paEntries[iEntry].Slot.awcName1, sizeof(paEntries[iEntry].Slot.awcName1));
2530 memcpy(&wszName[offName + 5 + 6], paEntries[iEntry].Slot.awcName2, sizeof(paEntries[iEntry].Slot.awcName2));
2531 }
2532 }
2533 /*
2534 * Regular directory entry. Do the matching, first 8.3 then long name.
2535 */
2536 else if ( fIs8Dot3Name
2537 && memcmp(paEntries[iEntry].Entry.achName, szName8Dot3, sizeof(paEntries[iEntry].Entry.achName)) == 0)
2538 {
2539 *poffEntryInDir = offEntryInDir;
2540 *pDirEntry = paEntries[iEntry].Entry;
2541 *pfLong = false;
2542 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2543 return VINF_SUCCESS;
2544 }
2545 else if ( cwcName != 0
2546 && idNextSlot == 0
2547 && rtFsFatDir_CalcChecksum(&paEntries[iEntry].Entry) == bChecksum
2548 && RTUtf16ICmpUtf8(wszName, pszEntry) == 0)
2549 {
2550 *poffEntryInDir = offEntryInDir;
2551 *pDirEntry = paEntries[iEntry].Entry;
2552 *pfLong = true;
2553 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2554 return VINF_SUCCESS;
2555 }
2556 else
2557 cwcName = 0;
2558 }
2559 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2560 }
2561
2562 return VERR_FILE_NOT_FOUND;
2563}
2564
2565
2566/**
2567 * Calculates the FATDIRENTRY::fCase flags for the given name.
2568 *
2569 * ASSUMES that the name is a 8.3 name.
2570 *
2571 * @returns Case flag mask.
2572 * @param pszName The name.
2573 */
2574static uint8_t rtFsFatDir_CalcCaseFlags(const char *pszName)
2575{
2576 uint8_t bRet = FATDIRENTRY_CASE_F_LOWER_BASE | FATDIRENTRY_CASE_F_LOWER_EXT;
2577 uint8_t bCurrent = FATDIRENTRY_CASE_F_LOWER_BASE;
2578 for (;;)
2579 {
2580 RTUNICP uc;
2581 int rc = RTStrGetCpEx(&pszName, &uc);
2582 if (RT_SUCCESS(rc))
2583 {
2584 if (uc != 0)
2585 {
2586 if (uc != '.')
2587 {
2588 if (RTUniCpIsUpper(uc))
2589 {
2590 bRet &= ~bCurrent;
2591 if (!bRet)
2592 return 0;
2593 }
2594 }
2595 else
2596 bCurrent = FATDIRENTRY_CASE_F_LOWER_EXT;
2597 }
2598 else if (bCurrent == FATDIRENTRY_CASE_F_LOWER_BASE)
2599 return bRet & ~FATDIRENTRY_CASE_F_LOWER_EXT;
2600 else
2601 return bRet;
2602 }
2603 else
2604 return 0;
2605 }
2606}
2607
2608
2609/**
2610 * Considers whether we need to create a long name or not.
2611 *
2612 * If a long name is needed and the name wasn't 8-dot-3 compatible, a 8-dot-3
2613 * name will be generated and stored in *pDirEntry.
2614 *
2615 * @returns IPRT status code
2616 * @param pThis The directory.
2617 * @param pszEntry The name.
2618 * @param fIs8Dot3Name Whether we have a 8-dot-3 name already.
2619 * @param pDirEntry Where to return the generated 8-dot-3 name.
2620 * @param paSlots Where to return the long name entries. The array
2621 * can hold at least FATDIRNAMESLOT_MAX_SLOTS entries.
2622 * @param pcSlots Where to return the actual number of slots used.
2623 */
2624static int rtFsFatDir_MaybeCreateLongNameAndShortAlias(PRTFSFATDIR pThis, const char *pszEntry, bool fIs8Dot3Name,
2625 PFATDIRENTRY pDirEntry, PFATDIRNAMESLOT paSlots, uint32_t *pcSlots)
2626{
2627 RT_NOREF(pThis, pDirEntry, paSlots, pszEntry);
2628 if (fIs8Dot3Name)
2629 {
2630 *pcSlots = 0;
2631 return VINF_SUCCESS;
2632 }
2633 *pcSlots = UINT32_MAX;
2634 return VERR_INVALID_NAME;
2635}
2636
2637
2638/**
2639 * Searches the directory for a given number of free directory entries.
2640 *
2641 * The free entries must be consecutive of course.
2642 *
2643 * @returns IPRT status code.
2644 * @retval VERR_DISK_FULL if no space was found, *pcFreeTail set.
2645 * @param pThis The directory to search.
2646 * @param cEntriesNeeded How many entries we need.
2647 * @param poffEntryInDir Where to return the offset of the first entry we
2648 * found.
2649 * @param pcFreeTail Where to return the number of free entries at the
2650 * end of the directory when VERR_DISK_FULL is
2651 * returned.
2652 */
2653static int rtFsFatChain_FindFreeEntries(PRTFSFATDIR pThis, uint32_t cEntriesNeeded,
2654 uint32_t *poffEntryInDir, uint32_t *pcFreeTail)
2655{
2656 /* First try make gcc happy. */
2657 *pcFreeTail = 0;
2658 *poffEntryInDir = UINT32_MAX;
2659
2660 /*
2661 * Scan the whole directory, buffer by buffer.
2662 */
2663 uint32_t offStartFreeEntries = UINT32_MAX;
2664 uint32_t cFreeEntries = 0;
2665 uint32_t offEntryInDir = 0;
2666 uint32_t const cbDir = pThis->Core.cbObject;
2667 Assert(RT_ALIGN_32(cbDir, sizeof(FATDIRENTRY)) == cbDir);
2668 while (offEntryInDir < cbDir)
2669 {
2670 /* Get chunk of entries starting at offEntryInDir. */
2671 uint32_t uBufferLock = UINT32_MAX;
2672 uint32_t cEntries = 0;
2673 PCFATDIRENTRYUNION paEntries = NULL;
2674 int rc = rtFsFatDir_GetEntriesAt(pThis, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
2675 if (RT_FAILURE(rc))
2676 return rc;
2677
2678 /*
2679 * Now work thru each of the entries.
2680 */
2681 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
2682 {
2683 uint8_t const bFirst = paEntries[iEntry].Entry.achName[0];
2684 if ( bFirst == FATDIRENTRY_CH0_DELETED
2685 || bFirst == FATDIRENTRY_CH0_END_OF_DIR)
2686 {
2687 if (offStartFreeEntries != UINT32_MAX)
2688 cFreeEntries++;
2689 else
2690 {
2691 offStartFreeEntries = offEntryInDir;
2692 cFreeEntries = 1;
2693 }
2694 if (cFreeEntries >= cEntriesNeeded)
2695 {
2696 *pcFreeTail = cEntriesNeeded;
2697 *poffEntryInDir = offStartFreeEntries;
2698 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2699 return VINF_SUCCESS;
2700 }
2701
2702 if (bFirst == FATDIRENTRY_CH0_END_OF_DIR)
2703 {
2704 if (pThis->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
2705 {
2706 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2707 *pcFreeTail = cFreeEntries = (cbDir - offStartFreeEntries) / sizeof(FATDIRENTRY);
2708 if (cFreeEntries >= cEntriesNeeded)
2709 {
2710 *poffEntryInDir = offStartFreeEntries;
2711 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2712 return VINF_SUCCESS;
2713 }
2714 return VERR_DISK_FULL;
2715 }
2716 }
2717 }
2718 else if (offStartFreeEntries != UINT32_MAX)
2719 {
2720 offStartFreeEntries = UINT32_MAX;
2721 cFreeEntries = 0;
2722 }
2723 }
2724 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
2725 }
2726 *pcFreeTail = cFreeEntries;
2727 return VERR_DISK_FULL;
2728}
2729
2730
2731/**
2732 * Try grow the directory.
2733 *
2734 * This is not called on the root directory.
2735 *
2736 * @returns IPRT status code.
2737 * @retval VERR_DISK_FULL if we failed to allocated new space.
2738 * @param pThis The directory to grow.
2739 * @param cMinNewEntries The minimum number of new entries to allocated.
2740 */
2741static int rtFsFatChain_GrowDirectory(PRTFSFATDIR pThis, uint32_t cMinNewEntries)
2742{
2743 RT_NOREF(pThis, cMinNewEntries);
2744 return VERR_DISK_FULL;
2745}
2746
2747
2748/**
2749 * Inserts a directory with zero of more long name slots preceeding it.
2750 *
2751 * @returns IPRT status code.
2752 * @param pThis The directory.
2753 * @param pDirEntry The directory entry.
2754 * @param paSlots The long name slots.
2755 * @param cSlots The number of long name slots.
2756 * @param poffEntryInDir Where to return the directory offset.
2757 */
2758static int rtFsFatChain_InsertEntries(PRTFSFATDIR pThis, PCFATDIRENTRY pDirEntry, PFATDIRNAMESLOT paSlots, uint32_t cSlots,
2759 uint32_t *poffEntryInDir)
2760{
2761 uint32_t const cTotalEntries = cSlots + 1;
2762
2763 /*
2764 * Find somewhere to put the entries. Try extend the directory if we're
2765 * not successful at first.
2766 */
2767 uint32_t cFreeTailEntries;
2768 uint32_t offFirstInDir;
2769 int rc = rtFsFatChain_FindFreeEntries(pThis, cTotalEntries, &offFirstInDir, &cFreeTailEntries);
2770 if (rc == VERR_DISK_FULL)
2771 {
2772 Assert(cFreeTailEntries < cTotalEntries);
2773
2774 /* Try grow it and use the newly allocated space. */
2775 if ( pThis->Core.pParentDir
2776 && pThis->cEntries < _64K /* Don't grow beyond 64K entries */)
2777 {
2778 offFirstInDir = pThis->Core.cbObject - cFreeTailEntries * sizeof(FATDIRENTRY);
2779 rc = rtFsFatChain_GrowDirectory(pThis, cTotalEntries - cFreeTailEntries);
2780 }
2781
2782 if (rc == VERR_DISK_FULL)
2783 {
2784 /** @todo Try compact the directory if we couldn't grow it. */
2785 }
2786 }
2787 if (RT_SUCCESS(rc))
2788 {
2789 /*
2790 * Update the directory.
2791 */
2792 uint32_t offCurrent = offFirstInDir;
2793 for (uint32_t iSrcSlot = 0; iSrcSlot < cTotalEntries; iSrcSlot++, offCurrent += sizeof(FATDIRENTRY))
2794 {
2795 uint32_t uBufferLock;
2796 PFATDIRENTRY pDstEntry;
2797 rc = rtFsFatDir_GetEntryForUpdate(pThis, offCurrent, &pDstEntry, &uBufferLock);
2798 if (RT_SUCCESS(rc))
2799 {
2800 if (iSrcSlot < cSlots)
2801 memcpy(pDstEntry, &paSlots[iSrcSlot], sizeof(*pDstEntry));
2802 else
2803 memcpy(pDstEntry, pDirEntry, sizeof(*pDstEntry));
2804 rc = rtFsFatDir_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
2805 if (RT_SUCCESS(rc))
2806 continue;
2807
2808 /*
2809 * Bail out: Try mark any edited entries as deleted.
2810 */
2811 iSrcSlot++;
2812 }
2813 while (iSrcSlot-- > 0)
2814 {
2815 int rc2 = rtFsFatDir_GetEntryForUpdate(pThis, offFirstInDir + iSrcSlot * sizeof(FATDIRENTRY),
2816 &pDstEntry, &uBufferLock);
2817 if (RT_SUCCESS(rc2))
2818 {
2819 pDstEntry->achName[0] = FATDIRENTRY_CH0_DELETED;
2820 rtFsFatDir_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
2821 }
2822 }
2823 *poffEntryInDir = UINT32_MAX;
2824 return rc;
2825 }
2826 AssertRC(rc);
2827
2828 /*
2829 * Successfully inserted all.
2830 */
2831 *poffEntryInDir = offFirstInDir + cSlots * sizeof(FATDIRENTRY);
2832 return VINF_SUCCESS;
2833 }
2834
2835 *poffEntryInDir = UINT32_MAX;
2836 return rc;
2837}
2838
2839
2840
2841/**
2842 * Creates a new directory entry.
2843 *
2844 * @returns IPRT status code
2845 * @param pThis The directory.
2846 * @param pszEntry The name of the new entry.
2847 * @param fAttrib The attributes.
2848 * @param cbInitial The initialize size.
2849 * @param poffEntryInDir Where to return the offset of the directory entry.
2850 * @param pDirEntry Where to return a copy of the directory entry.
2851 *
2852 * @remarks ASSUMES caller has already called rtFsFatDir_FindEntry to make sure
2853 * the entry doesn't exist.
2854 */
2855static int rtFsFatDir_CreateEntry(PRTFSFATDIR pThis, const char *pszEntry, uint8_t fAttrib, uint32_t cbInitial,
2856 uint32_t *poffEntryInDir, PFATDIRENTRY pDirEntry)
2857{
2858 PRTFSFATVOL pVol = pThis->Core.pVol;
2859 *poffEntryInDir = UINT32_MAX;
2860 if (pVol->fReadOnly)
2861 return VERR_WRITE_PROTECT;
2862
2863 /*
2864 * Create the directory entries on the stack.
2865 */
2866 bool fIs8Dot3Name = rtFsFatDir_StringTo8Dot3((char *)pDirEntry->achName, pszEntry);
2867 pDirEntry->fAttrib = fAttrib;
2868 pDirEntry->fCase = fIs8Dot3Name ? rtFsFatDir_CalcCaseFlags(pszEntry) : 0;
2869 pDirEntry->uBirthCentiseconds = rtFsFatCurrentFatDateTime(pVol, &pDirEntry->uBirthDate, &pDirEntry->uBirthTime);
2870 pDirEntry->uAccessDate = pDirEntry->uBirthDate;
2871 pDirEntry->uModifyDate = pDirEntry->uBirthDate;
2872 pDirEntry->uModifyTime = pDirEntry->uBirthTime;
2873 pDirEntry->idxCluster = 0; /* Will fill this in later if cbInitial is non-zero. */
2874 pDirEntry->u.idxClusterHigh = 0;
2875 pDirEntry->cbFile = cbInitial;
2876
2877 /*
2878 * Create long filename slots if necessary.
2879 */
2880 uint32_t cSlots = UINT32_MAX;
2881 FATDIRNAMESLOT aSlots[FATDIRNAMESLOT_MAX_SLOTS];
2882 int rc = rtFsFatDir_MaybeCreateLongNameAndShortAlias(pThis, pszEntry, fIs8Dot3Name, pDirEntry, aSlots, &cSlots);
2883 if (RT_SUCCESS(rc))
2884 {
2885 Assert(cSlots <= FATDIRNAMESLOT_MAX_SLOTS);
2886
2887 /*
2888 * Allocate initial clusters if requested.
2889 */
2890 RTFSFATCHAIN Clusters;
2891 rtFsFatChain_InitEmpty(&Clusters, pVol);
2892 if (cbInitial > 0)
2893 {
2894 rc = rtFsFatClusterMap_AllocateMoreClusters(pVol, &Clusters,
2895 (cbInitial + Clusters.cbCluster - 1) >> Clusters.cClusterByteShift);
2896 if (RT_SUCCESS(rc))
2897 {
2898 uint32_t idxFirstCluster = rtFsFatChain_GetFirstCluster(&Clusters);
2899 pDirEntry->idxCluster = (uint16_t)idxFirstCluster;
2900 if (pVol->enmFatType >= RTFSFATTYPE_FAT32)
2901 pDirEntry->u.idxClusterHigh = (uint16_t)(idxFirstCluster >> 16);
2902 }
2903 }
2904 if (RT_SUCCESS(rc))
2905 {
2906 /*
2907 * Insert the directory entry and name slots.
2908 */
2909 rc = rtFsFatChain_InsertEntries(pThis, pDirEntry, aSlots, cSlots, poffEntryInDir);
2910 if (RT_SUCCESS(rc))
2911 {
2912 rtFsFatChain_Delete(&Clusters);
2913 return VINF_SUCCESS;
2914 }
2915
2916 for (uint32_t iClusterToFree = 0; iClusterToFree < Clusters.cClusters; iClusterToFree++)
2917 rtFsFatClusterMap_FreeCluster(pVol, rtFsFatChain_GetClusterByIndex(&Clusters, iClusterToFree));
2918 rtFsFatChain_Delete(&Clusters);
2919 }
2920 }
2921 else
2922 rc = VERR_NET_NOT_UNIQUE_NAME;
2923 return rc;
2924}
2925
2926
2927
2928/**
2929 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2930 */
2931static DECLCALLBACK(int) rtFsFatDir_Close(void *pvThis)
2932{
2933 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
2934 int rc;
2935 if (pThis->paEntries)
2936 {
2937 rc = rtFsFatDir_Flush(pThis);
2938 RTMemFree(pThis->paEntries);
2939 pThis->paEntries = NULL;
2940 }
2941 else
2942 rc = VINF_SUCCESS;
2943
2944 rtFsFatObj_Close(&pThis->Core);
2945 return rc;
2946}
2947
2948
2949/**
2950 * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
2951 */
2952static DECLCALLBACK(int) rtFsFatDir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
2953 PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
2954{
2955 /*
2956 * FAT doesn't do symbolic links and mounting file systems within others
2957 * haven't been implemented yet, I think, so only care if a directory is
2958 * asked for.
2959 */
2960 int rc;
2961 if (phVfsSymlink)
2962 *phVfsSymlink = NIL_RTVFSSYMLINK;
2963 if (phVfsMounted)
2964 *phVfsMounted = NIL_RTVFS;
2965 if (phVfsDir)
2966 {
2967 *phVfsDir = NIL_RTVFSDIR;
2968
2969 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
2970 uint32_t offEntryInDir;
2971 bool fLong;
2972 FATDIRENTRY DirEntry;
2973 rc = rtFsFatDir_FindEntry(pThis, pszEntry, &offEntryInDir, &fLong, &DirEntry);
2974 if (RT_SUCCESS(rc))
2975 {
2976 switch (DirEntry.fAttrib & (FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME))
2977 {
2978 case FAT_ATTR_DIRECTORY:
2979 {
2980 rc = rtFsFatDir_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir,
2981 RTFSFAT_GET_CLUSTER(&DirEntry, pThis->Core.pVol), UINT64_MAX /*offDisk*/,
2982 DirEntry.cbFile, phVfsDir, NULL /*ppDir*/);
2983 break;
2984 }
2985 case 0:
2986 rc = VERR_NOT_A_DIRECTORY;
2987 break;
2988 default:
2989 rc = VERR_PATH_NOT_FOUND;
2990 break;
2991 }
2992 }
2993 else if (rc == VERR_FILE_NOT_FOUND)
2994 rc = VERR_PATH_NOT_FOUND;
2995 }
2996 else
2997 rc = VERR_PATH_NOT_FOUND;
2998 return rc;
2999}
3000
3001
3002/**
3003 * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
3004 */
3005static DECLCALLBACK(int) rtFsFatDir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
3006{
3007 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3008
3009 /*
3010 * Try open existing file.
3011 */
3012 uint32_t offEntryInDir;
3013 bool fLong;
3014 FATDIRENTRY DirEntry;
3015 int rc = rtFsFatDir_FindEntry(pThis, pszFilename, &offEntryInDir, &fLong, &DirEntry);
3016 if (RT_SUCCESS(rc))
3017 {
3018 switch (DirEntry.fAttrib & (FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME))
3019 {
3020 case 0:
3021 if ( !(DirEntry.fAttrib & FAT_ATTR_READONLY)
3022 || !(fOpen & RTFILE_O_WRITE))
3023 {
3024 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
3025 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
3026 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
3027 rc = rtFsFatFile_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir, fOpen, phVfsFile);
3028 else
3029 rc = VERR_ALREADY_EXISTS;
3030 }
3031 else
3032 rc = VERR_ACCESS_DENIED;
3033 break;
3034
3035 case FAT_ATTR_DIRECTORY:
3036 rc = VERR_NOT_A_FILE;
3037 break;
3038 default:
3039 rc = VERR_PATH_NOT_FOUND;
3040 break;
3041 }
3042 }
3043 /*
3044 * Create the file?
3045 */
3046 else if ( rc == VERR_FILE_NOT_FOUND
3047 && ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
3048 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
3049 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE) )
3050 {
3051 rc = rtFsFatDir_CreateEntry(pThis, pszFilename, FAT_ATTR_ARCHIVE, 0 /*cbInitial*/, &offEntryInDir, &DirEntry);
3052 if (RT_SUCCESS(rc))
3053 rc = rtFsFatFile_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir, fOpen, phVfsFile);
3054 }
3055 return rc;
3056}
3057
3058
3059/**
3060 * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
3061 */
3062static DECLCALLBACK(int) rtFsFatDir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
3063{
3064 RT_NOREF(pvThis, pszSubDir, fFlags, phVfsDir);
3065RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSubDir);
3066 return VERR_NOT_IMPLEMENTED;
3067}
3068
3069
3070/**
3071 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3072 */
3073static DECLCALLBACK(int) rtFsFatDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3074{
3075 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3076RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSubDir);
3077 return VERR_NOT_IMPLEMENTED;
3078}
3079
3080
3081/**
3082 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3083 */
3084static DECLCALLBACK(int) rtFsFatDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3085{
3086 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3087RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
3088 return VERR_NOT_SUPPORTED;
3089}
3090
3091
3092/**
3093 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3094 */
3095static DECLCALLBACK(int) rtFsFatDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3096 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3097{
3098 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3099RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
3100 return VERR_NOT_SUPPORTED;
3101}
3102
3103
3104/**
3105 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3106 */
3107static DECLCALLBACK(int) rtFsFatDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3108{
3109 RT_NOREF(pvThis, pszEntry, fType);
3110RTAssertMsg2("%s: %s\n", __FUNCTION__, pszEntry);
3111 return VERR_NOT_IMPLEMENTED;
3112}
3113
3114
3115/**
3116 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3117 */
3118static DECLCALLBACK(int) rtFsFatDir_RewindDir(void *pvThis)
3119{
3120 RT_NOREF(pvThis);
3121RTAssertMsg2("%s\n", __FUNCTION__);
3122 return VERR_NOT_IMPLEMENTED;
3123}
3124
3125
3126/**
3127 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3128 */
3129static DECLCALLBACK(int) rtFsFatDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3130 RTFSOBJATTRADD enmAddAttr)
3131{
3132 RT_NOREF(pvThis, pDirEntry, pcbDirEntry, enmAddAttr);
3133RTAssertMsg2("%s\n", __FUNCTION__);
3134 return VERR_NOT_IMPLEMENTED;
3135}
3136
3137
3138/**
3139 * FAT file operations.
3140 */
3141static const RTVFSDIROPS g_rtFsFatDirOps =
3142{
3143 { /* Obj */
3144 RTVFSOBJOPS_VERSION,
3145 RTVFSOBJTYPE_DIR,
3146 "FatDir",
3147 rtFsFatDir_Close,
3148 rtFsFatObj_QueryInfo,
3149 RTVFSOBJOPS_VERSION
3150 },
3151 RTVFSDIROPS_VERSION,
3152 0,
3153 { /* ObjSet */
3154 RTVFSOBJSETOPS_VERSION,
3155 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
3156 rtFsFatObj_SetMode,
3157 rtFsFatObj_SetTimes,
3158 rtFsFatObj_SetOwner,
3159 RTVFSOBJSETOPS_VERSION
3160 },
3161 rtFsFatDir_TraversalOpen,
3162 rtFsFatDir_OpenFile,
3163 rtFsFatDir_OpenDir,
3164 rtFsFatDir_CreateDir,
3165 rtFsFatDir_OpenSymlink,
3166 rtFsFatDir_CreateSymlink,
3167 rtFsFatDir_UnlinkEntry,
3168 rtFsFatDir_RewindDir,
3169 rtFsFatDir_ReadDir,
3170 RTVFSDIROPS_VERSION,
3171};
3172
3173
3174/**
3175 * Adds an open child to the parent directory.
3176 *
3177 * Maintains an additional reference to the parent dir to prevent it from going
3178 * away. If @a pDir is the root directory, it also ensures the volume is
3179 * referenced and sticks around until the last open object is gone.
3180 *
3181 * @param pDir The directory.
3182 * @param pChild The child being opened.
3183 * @sa rtFsFatDir_RemoveOpenChild
3184 */
3185static void rtFsFatDir_AddOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild)
3186{
3187 /* First child that gets opened retains the parent directory. This is
3188 released by the final open child. */
3189 if (RTListIsEmpty(&pDir->OpenChildren))
3190 {
3191 uint32_t cRefs = RTVfsDirRetain(pDir->hVfsSelf);
3192 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3193
3194 /* Root also retains the whole file system. */
3195 if (!pDir->Core.pParentDir)
3196 {
3197 Assert(pDir->Core.pVol);
3198 Assert(pDir->Core.pVol == pChild->pVol);
3199 cRefs = RTVfsRetain(pDir->Core.pVol->hVfsSelf);
3200 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3201 }
3202 }
3203 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
3204 pChild->pParentDir = pDir;
3205}
3206
3207
3208/**
3209 * Removes an open child to the parent directory.
3210 *
3211 * @param pDir The directory.
3212 * @param pChild The child being removed.
3213 *
3214 * @remarks This is the very last thing you do as it may cause a few other
3215 * objects to be released recursively (parent dir and the volume).
3216 *
3217 * @sa rtFsFatDir_AddOpenChild
3218 */
3219static void rtFsFatDir_RemoveOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild)
3220{
3221 AssertReturnVoid(pChild->pParentDir == pDir);
3222 RTListNodeRemove(&pChild->Entry);
3223 pChild->pParentDir = NULL;
3224
3225 /* Final child? If so, release directory. */
3226 if (RTListIsEmpty(&pDir->OpenChildren))
3227 {
3228 uint32_t cRefs = RTVfsDirRelease(pDir->hVfsSelf);
3229 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3230
3231 /* Root directory releases the file system as well. Since the volume
3232 holds a reference to the root directory, it will remain valid after
3233 the above release. */
3234 if (!pDir->Core.pParentDir)
3235 {
3236 Assert(cRefs > 0);
3237 Assert(pDir->Core.pVol);
3238 Assert(pDir->Core.pVol == pChild->pVol);
3239 cRefs = RTVfsRetain(pDir->Core.pVol->hVfsSelf);
3240 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3241 }
3242 }
3243}
3244
3245
3246/**
3247 * Instantiates a new directory.
3248 *
3249 * @returns IPRT status code.
3250 * @param pThis The FAT volume instance.
3251 * @param pParentDir The parent directory. This is NULL for the root
3252 * directory.
3253 * @param pDirEntry The parent directory entry. This is NULL for the
3254 * root directory.
3255 * @param offEntryInDir The byte offset of the directory entry in the parent
3256 * directory. UINT32_MAX if root directory.
3257 * @param idxCluster The cluster where the directory content is to be
3258 * found. This can be UINT32_MAX if a root FAT12/16
3259 * directory.
3260 * @param offDisk The disk byte offset of the FAT12/16 root directory.
3261 * This is UINT64_MAX if idxCluster is given.
3262 * @param cbDir The size of the directory.
3263 * @param phVfsDir Where to return the directory handle.
3264 * @param ppDir Where to return the FAT directory instance data.
3265 */
3266static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
3267 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir, PRTFSFATDIR *ppDir)
3268{
3269 Assert((idxCluster == UINT32_MAX) != (offDisk == UINT64_MAX));
3270 Assert((pDirEntry == NULL) == (offEntryInDir == UINT32_MAX));
3271 if (ppDir)
3272 *ppDir = NULL;
3273
3274 PRTFSFATDIR pNewDir;
3275 int rc = RTVfsNewDir(&g_rtFsFatDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
3276 phVfsDir, (void **)&pNewDir);
3277 if (RT_SUCCESS(rc))
3278 {
3279 /*
3280 * Initialize it all so rtFsFatDir_Close doesn't trip up in anyway.
3281 */
3282 RTListInit(&pNewDir->OpenChildren);
3283 if (pDirEntry)
3284 rtFsFatObj_InitFromDirEntry(&pNewDir->Core, pDirEntry, offEntryInDir, pThis);
3285 else
3286 rtFsFatObj_InitDummy(&pNewDir->Core, cbDir, RTFS_DOS_DIRECTORY, pThis);
3287
3288 pNewDir->hVfsSelf = *phVfsDir;
3289 pNewDir->cEntries = cbDir / sizeof(FATDIRENTRY);
3290 pNewDir->fIsLinearRootDir = idxCluster == UINT32_MAX;
3291 pNewDir->fFullyBuffered = pNewDir->fIsLinearRootDir;
3292 pNewDir->paEntries = NULL;
3293 pNewDir->offEntriesOnDisk = UINT64_MAX;
3294 if (pNewDir->fFullyBuffered)
3295 pNewDir->cbAllocatedForEntries = RT_ALIGN_32(cbDir, pThis->cbSector);
3296 else
3297 pNewDir->cbAllocatedForEntries = pThis->cbSector;
3298
3299 /*
3300 * If clustered backing, read the chain and see if we cannot still do the full buffering.
3301 */
3302 if (idxCluster != UINT32_MAX)
3303 {
3304 rc = rtFsFatClusterMap_ReadClusterChain(pThis, idxCluster, &pNewDir->Core.Clusters);
3305 if (RT_SUCCESS(rc))
3306 {
3307 if ( pNewDir->Core.Clusters.cClusters >= 1
3308 && pNewDir->Core.Clusters.cbChain <= _64K
3309 && rtFsFatChain_IsContiguous(&pNewDir->Core.Clusters))
3310 {
3311 Assert(pNewDir->Core.Clusters.cbChain >= cbDir);
3312 pNewDir->cbAllocatedForEntries = pNewDir->Core.Clusters.cbChain;
3313 pNewDir->fFullyBuffered = true;
3314 }
3315 }
3316 }
3317 else
3318 rtFsFatChain_InitEmpty(&pNewDir->Core.Clusters, pThis);
3319 if (RT_SUCCESS(rc))
3320 {
3321 /*
3322 * Allocate and initialize the buffering. Fill the buffer.
3323 */
3324 pNewDir->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pNewDir->cbAllocatedForEntries);
3325 if (!pNewDir->paEntries)
3326 {
3327 if (pNewDir->fFullyBuffered && !pNewDir->fIsLinearRootDir)
3328 {
3329 pNewDir->fFullyBuffered = false;
3330 pNewDir->cbAllocatedForEntries = pThis->cbSector;
3331 pNewDir->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pNewDir->cbAllocatedForEntries);
3332 }
3333 if (!pNewDir->paEntries)
3334 rc = VERR_NO_MEMORY;
3335 }
3336
3337 if (RT_SUCCESS(rc))
3338 {
3339 if (pNewDir->fFullyBuffered)
3340 {
3341 pNewDir->u.Full.cDirtySectors = 0;
3342 pNewDir->u.Full.cSectors = pNewDir->cbAllocatedForEntries / pThis->cbSector;
3343 pNewDir->u.Full.pbDirtySectors = (uint8_t *)RTMemAllocZ((pNewDir->u.Full.cSectors + 63) / 8);
3344 if (pNewDir->u.Full.pbDirtySectors)
3345 pNewDir->offEntriesOnDisk = offDisk != UINT64_MAX ? offDisk
3346 : rtFsFatClusterToDiskOffset(pThis, idxCluster);
3347 else
3348 rc = VERR_NO_MEMORY;
3349 }
3350 else
3351 {
3352 pNewDir->offEntriesOnDisk = rtFsFatClusterToDiskOffset(pThis, idxCluster);
3353 pNewDir->u.Simple.offInDir = 0;
3354 pNewDir->u.Simple.fDirty = false;
3355 }
3356 if (RT_SUCCESS(rc))
3357 rc = RTVfsFileReadAt(pThis->hVfsBacking, pNewDir->offEntriesOnDisk,
3358 pNewDir->paEntries, pNewDir->cbAllocatedForEntries, NULL);
3359 if (RT_SUCCESS(rc))
3360 {
3361 /*
3362 * Link into parent directory so we can use it to update
3363 * our directory entry.
3364 */
3365 if (pParentDir)
3366 rtFsFatDir_AddOpenChild(pParentDir, &pNewDir->Core);
3367 if (ppDir)
3368 *ppDir = pNewDir;
3369 return VINF_SUCCESS;
3370 }
3371 }
3372
3373 /* Free the buffer on failure so rtFsFatDir_Close doesn't try do anything with it. */
3374 RTMemFree(pNewDir->paEntries);
3375 pNewDir->paEntries = NULL;
3376 }
3377
3378 RTVfsDirRelease(*phVfsDir);
3379 }
3380 *phVfsDir = NIL_RTVFSDIR;
3381 if (ppDir)
3382 *ppDir = NULL;
3383 return rc;
3384}
3385
3386
3387
3388
3389
3390/**
3391 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
3392 */
3393static DECLCALLBACK(int) rtFsFatVol_Close(void *pvThis)
3394{
3395 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
3396 int rc = rtFsFatClusterMap_Destroy(pThis);
3397
3398 if (pThis->hVfsRootDir != NIL_RTVFSDIR)
3399 {
3400 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
3401 uint32_t cRefs = RTVfsDirRelease(pThis->hVfsRootDir);
3402 Assert(cRefs == 0); NOREF(cRefs);
3403 pThis->hVfsRootDir = NIL_RTVFSDIR;
3404 pThis->pRootDir = NULL;
3405 }
3406
3407 RTVfsFileRelease(pThis->hVfsBacking);
3408 pThis->hVfsBacking = NIL_RTVFSFILE;
3409
3410 return rc;
3411}
3412
3413
3414/**
3415 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
3416 */
3417static DECLCALLBACK(int) rtFsFatVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3418{
3419 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
3420 return VERR_WRONG_TYPE;
3421}
3422
3423
3424/**
3425 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
3426 */
3427static DECLCALLBACK(int) rtFsFatVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
3428{
3429 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
3430 uint32_t cRefs = RTVfsDirRetain(pThis->hVfsRootDir);
3431 if (cRefs != UINT32_MAX)
3432 {
3433 *phVfsDir = pThis->hVfsRootDir;
3434 return VINF_SUCCESS;
3435 }
3436 return VERR_INTERNAL_ERROR_5;
3437}
3438
3439
3440/**
3441 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
3442 */
3443static DECLCALLBACK(int) rtFsFatVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
3444{
3445 RT_NOREF(pvThis, off, cb, pfUsed);
3446 return VERR_NOT_IMPLEMENTED;
3447}
3448
3449
3450DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsFatVolOps =
3451{
3452 { /* Obj */
3453 RTVFSOBJOPS_VERSION,
3454 RTVFSOBJTYPE_VFS,
3455 "FatVol",
3456 rtFsFatVol_Close,
3457 rtFsFatVol_QueryInfo,
3458 RTVFSOBJOPS_VERSION
3459 },
3460 RTVFSOPS_VERSION,
3461 0 /* fFeatures */,
3462 rtFsFatVol_OpenRoot,
3463 rtFsFatVol_IsRangeInUse,
3464 RTVFSOPS_VERSION
3465};
3466
3467
3468/**
3469 * Tries to detect a DOS 1.x formatted image and fills in the BPB fields.
3470 *
3471 * There is no BPB here, but fortunately, there isn't much variety.
3472 *
3473 * @returns IPRT status code.
3474 * @param pThis The FAT volume instance, BPB derived fields are filled
3475 * in on success.
3476 * @param pBootSector The boot sector.
3477 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
3478 * the boot sector.
3479 * @param pErrInfo Where to return additional error information.
3480 */
3481static int rtFsFatVolTryInitDos1x(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t const *pbFatSector,
3482 PRTERRINFO pErrInfo)
3483{
3484 /*
3485 * PC-DOS 1.0 does a 2fh byte short jump w/o any NOP following it.
3486 * Instead the following are three words and a 9 byte build date
3487 * string. The remaining space is zero filled.
3488 *
3489 * Note! No idea how this would look like for 8" floppies, only got 5"1/4'.
3490 *
3491 * ASSUME all non-BPB disks are using this format.
3492 */
3493 if ( pBootSector->abJmp[0] != 0xeb /* jmp rel8 */
3494 || pBootSector->abJmp[1] < 0x2f
3495 || pBootSector->abJmp[1] >= 0x80
3496 || pBootSector->abJmp[2] == 0x90 /* nop */)
3497 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3498 "No DOS v1.0 bootsector either - invalid jmp: %.3Rhxs", pBootSector->abJmp);
3499 uint32_t const offJump = 2 + pBootSector->abJmp[1];
3500 uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
3501 Assert(offFirstZero >= RT_UOFFSETOF(FATBOOTSECTOR, Bpb));
3502 uint32_t const cbZeroPad = RT_MIN(offJump - offFirstZero,
3503 sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_OFFSETOF(FATBOOTSECTOR, Bpb)));
3504
3505 if (!ASMMemIsAllU8((uint8_t const *)pBootSector + offFirstZero, cbZeroPad, 0))
3506 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3507 "No DOS v1.0 bootsector either - expected zero padding %#x LB %#x: %.*Rhxs",
3508 offFirstZero, cbZeroPad, cbZeroPad, (uint8_t const *)pBootSector + offFirstZero);
3509
3510 /*
3511 * Check the FAT ID so we can tell if this is double or single sided,
3512 * as well as being a valid FAT12 start.
3513 */
3514 if ( (pbFatSector[0] != 0xfe && pbFatSector[0] != 0xff)
3515 || pbFatSector[1] != 0xff
3516 || pbFatSector[2] != 0xff)
3517 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3518 "No DOS v1.0 bootsector either - unexpected start of FAT: %.3Rhxs", pbFatSector);
3519
3520 /*
3521 * Fixed DOS 1.0 config.
3522 */
3523 pThis->enmFatType = RTFSFATTYPE_FAT12;
3524 pThis->enmBpbVersion = RTFSFATBPBVER_NO_BPB;
3525 pThis->bMedia = pbFatSector[0];
3526 pThis->cReservedSectors = 1;
3527 pThis->cbSector = 512;
3528 pThis->cbCluster = pThis->bMedia == 0xfe ? 1024 : 512;
3529 pThis->cFats = 2;
3530 pThis->cbFat = 512;
3531 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * 512;
3532 pThis->aoffFats[1] = pThis->aoffFats[0] + pThis->cbFat;
3533 pThis->offRootDir = pThis->aoffFats[1] + pThis->cbFat;
3534 pThis->cRootDirEntries = 512;
3535 pThis->offFirstCluster = pThis->offRootDir + RT_ALIGN_32(pThis->cRootDirEntries * sizeof(FATDIRENTRY),
3536 pThis->cbSector);
3537 pThis->cbTotalSize = pThis->bMedia == 0xfe ? 8 * 1 * 40 * 512 : 8 * 2 * 40 * 512;
3538 pThis->cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
3539 return VINF_SUCCESS;
3540}
3541
3542
3543/**
3544 * Worker for rtFsFatVolTryInitDos2Plus that handles remaining BPB fields.
3545 *
3546 * @returns IPRT status code.
3547 * @param pThis The FAT volume instance, BPB derived fields are filled
3548 * in on success.
3549 * @param pBootSector The boot sector.
3550 * @param fMaybe331 Set if it could be a DOS v3.31 BPB.
3551 * @param pErrInfo Where to return additional error information.
3552 */
3553static int rtFsFatVolTryInitDos2PlusBpb(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, bool fMaybe331, PRTERRINFO pErrInfo)
3554{
3555 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_2_0;
3556
3557 /*
3558 * Figure total sector count. Could both be zero, in which case we have to
3559 * fall back on the size of the backing stuff.
3560 */
3561 if (pBootSector->Bpb.Bpb20.cTotalSectors16 != 0)
3562 pThis->cbTotalSize = pBootSector->Bpb.Bpb20.cTotalSectors16 * pThis->cbSector;
3563 else if ( pBootSector->Bpb.Bpb331.cTotalSectors32 != 0
3564 && fMaybe331)
3565 {
3566 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_3_31;
3567 pThis->cbTotalSize = pBootSector->Bpb.Bpb331.cTotalSectors32 * (uint64_t)pThis->cbSector;
3568 }
3569 else
3570 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
3571 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
3572 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3573 "Bogus FAT12/16 total or reserved sector count: %#x vs %#x",
3574 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
3575
3576 /*
3577 * The fat size. Complete FAT offsets.
3578 */
3579 if ( pBootSector->Bpb.Bpb20.cSectorsPerFat == 0
3580 || ((uint32_t)pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cFats + 1) * pThis->cbSector > pThis->cbTotalSize)
3581 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 sectors per FAT: %#x (total sectors %#RX64)",
3582 pBootSector->Bpb.Bpb20.cSectorsPerFat, pThis->cbTotalSize / pThis->cbSector);
3583 pThis->cbFat = pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cbSector;
3584
3585 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
3586 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
3587 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
3588
3589 /*
3590 * Do root directory calculations.
3591 */
3592 pThis->idxRootDirCluster = UINT32_MAX;
3593 pThis->offRootDir = pThis->aoffFats[pThis->cFats];
3594 if (pThis->cRootDirEntries == 0)
3595 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero FAT12/16 root directory size");
3596 pThis->cbRootDir = pThis->cRootDirEntries * sizeof(FATDIRENTRY);
3597 pThis->cbRootDir = RT_ALIGN_32(pThis->cbRootDir, pThis->cbSector);
3598
3599 /*
3600 * First cluster and cluster count checks and calcs. Determin FAT type.
3601 */
3602 pThis->offFirstCluster = pThis->offRootDir + pThis->cbRootDir;
3603 uint64_t cbSystemStuff = pThis->offFirstCluster - pThis->offBootSector;
3604 if (cbSystemStuff >= pThis->cbTotalSize)
3605 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 total size, root dir, or fat size");
3606 pThis->cClusters = (pThis->cbTotalSize - cbSystemStuff) / pThis->cbCluster;
3607
3608 if (pThis->cClusters >= FAT_MAX_FAT16_DATA_CLUSTERS)
3609 {
3610 pThis->cClusters = FAT_MAX_FAT16_DATA_CLUSTERS;
3611 pThis->enmFatType = RTFSFATTYPE_FAT16;
3612 }
3613 else if (pThis->cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS)
3614 pThis->enmFatType = RTFSFATTYPE_FAT16;
3615 else
3616 pThis->enmFatType = RTFSFATTYPE_FAT12; /** @todo Not sure if this is entirely the right way to go about it... */
3617
3618 uint32_t cClustersPerFat;
3619 if (pThis->enmFatType == RTFSFATTYPE_FAT16)
3620 cClustersPerFat = pThis->cbFat / 2;
3621 else
3622 cClustersPerFat = pThis->cbFat * 2 / 3;
3623 if (pThis->cClusters > cClustersPerFat)
3624 pThis->cClusters = cClustersPerFat;
3625
3626 return VINF_SUCCESS;
3627}
3628
3629
3630/**
3631 * Worker for rtFsFatVolTryInitDos2Plus and rtFsFatVolTryInitDos2PlusFat32 that
3632 * handles common extended BPBs fields.
3633 *
3634 * @returns IPRT status code.
3635 * @param pThis The FAT volume instance.
3636 * @param bExtSignature The extended BPB signature.
3637 * @param uSerialNumber The serial number.
3638 * @param pachLabel Pointer to the volume label field.
3639 * @param pachType Pointer to the file system type field.
3640 */
3641static void rtFsFatVolInitCommonEbpbBits(PRTFSFATVOL pThis, uint8_t bExtSignature, uint32_t uSerialNumber,
3642 char const *pachLabel, char const *pachType)
3643{
3644 pThis->uSerialNo = uSerialNumber;
3645 if (bExtSignature == FATEBPB_SIGNATURE)
3646 {
3647 memcpy(pThis->szLabel, pachLabel, RT_SIZEOFMEMB(FATEBPB, achLabel));
3648 pThis->szLabel[RT_SIZEOFMEMB(FATEBPB, achLabel)] = '\0';
3649 RTStrStrip(pThis->szLabel);
3650
3651 memcpy(pThis->szType, pachType, RT_SIZEOFMEMB(FATEBPB, achType));
3652 pThis->szType[RT_SIZEOFMEMB(FATEBPB, achType)] = '\0';
3653 RTStrStrip(pThis->szType);
3654 }
3655 else
3656 {
3657 pThis->szLabel[0] = '\0';
3658 pThis->szType[0] = '\0';
3659 }
3660}
3661
3662
3663/**
3664 * Worker for rtFsFatVolTryInitDos2Plus that deals with FAT32.
3665 *
3666 * @returns IPRT status code.
3667 * @param pThis The FAT volume instance, BPB derived fields are filled
3668 * in on success.
3669 * @param pBootSector The boot sector.
3670 * @param pErrInfo Where to return additional error information.
3671 */
3672static int rtFsFatVolTryInitDos2PlusFat32(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, PRTERRINFO pErrInfo)
3673{
3674 pThis->enmFatType = RTFSFATTYPE_FAT32;
3675 pThis->enmBpbVersion = pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE
3676 ? RTFSFATBPBVER_FAT32_29 : RTFSFATBPBVER_FAT32_28;
3677 pThis->fFat32Flags = pBootSector->Bpb.Fat32Ebpb.fFlags;
3678
3679 if (pBootSector->Bpb.Fat32Ebpb.uVersion != FAT32EBPB_VERSION_0_0)
3680 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Unsupported FAT32 version: %d.%d (%#x)",
3681 RT_HI_U8(pBootSector->Bpb.Fat32Ebpb.uVersion), RT_LO_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),
3682 pBootSector->Bpb.Fat32Ebpb.uVersion);
3683
3684 /*
3685 * Figure total sector count. We expected it to be filled in.
3686 */
3687 bool fUsing64BitTotalSectorCount = false;
3688 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 != 0)
3689 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 * pThis->cbSector;
3690 else if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 != 0)
3691 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 * (uint64_t)pThis->cbSector;
3692 else if ( pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 <= UINT64_MAX / 512
3693 && pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 > 3
3694 && pBootSector->Bpb.Fat32Ebpb.bExtSignature != FATEBPB_SIGNATURE_OLD)
3695 {
3696 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 * pThis->cbSector;
3697 fUsing64BitTotalSectorCount = true;
3698 }
3699 else
3700 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 total sector count out of range: %#RX64",
3701 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64);
3702 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
3703 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3704 "Bogus FAT32 total or reserved sector count: %#x vs %#x",
3705 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
3706
3707 /*
3708 * Fat size. We check the 16-bit field even if it probably should be zero all the time.
3709 */
3710 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat != 0)
3711 {
3712 if ( pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != 0
3713 && pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat)
3714 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3715 "Both 16-bit and 32-bit FAT size fields are set: %#RX16 vs %#RX32",
3716 pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat, pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
3717 pThis->cbFat = pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat * pThis->cbSector;
3718 }
3719 else
3720 {
3721 uint64_t cbFat = pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 * (uint64_t)pThis->cbSector;
3722 if ( cbFat == 0
3723 || cbFat >= FAT_MAX_FAT32_TOTAL_CLUSTERS * 4 + pThis->cbSector * 16)
3724 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3725 "Bogus 32-bit FAT size: %#RX32", pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
3726 pThis->cbFat = (uint32_t)cbFat;
3727 }
3728
3729 /*
3730 * Complete the FAT offsets and first cluster offset, then calculate number
3731 * of data clusters.
3732 */
3733 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
3734 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
3735 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
3736 pThis->offFirstCluster = pThis->aoffFats[pThis->cFats];
3737
3738 if (pThis->offFirstCluster - pThis->offBootSector >= pThis->cbTotalSize)
3739 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3740 "Bogus 32-bit FAT size or total sector count: cFats=%d cbFat=%#x cbTotalSize=%#x",
3741 pThis->cFats, pThis->cbFat, pThis->cbTotalSize);
3742
3743 uint64_t cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
3744 if (cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS)
3745 pThis->cClusters = (uint32_t)cClusters;
3746 else
3747 pThis->cClusters = FAT_MAX_FAT32_DATA_CLUSTERS;
3748 if (pThis->cClusters > (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER))
3749 pThis->cClusters = (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER);
3750
3751 /*
3752 * Root dir cluster.
3753 */
3754 if ( pBootSector->Bpb.Fat32Ebpb.uRootDirCluster < FAT_FIRST_DATA_CLUSTER
3755 || pBootSector->Bpb.Fat32Ebpb.uRootDirCluster >= pThis->cClusters)
3756 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3757 "Bogus FAT32 root directory cluster: %#x", pBootSector->Bpb.Fat32Ebpb.uRootDirCluster);
3758 pThis->idxRootDirCluster = pBootSector->Bpb.Fat32Ebpb.uRootDirCluster;
3759 pThis->offRootDir = pThis->offFirstCluster
3760 + (pBootSector->Bpb.Fat32Ebpb.uRootDirCluster - FAT_FIRST_DATA_CLUSTER) * pThis->cbCluster;
3761
3762 /*
3763 * Info sector.
3764 */
3765 if ( pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == 0
3766 || pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == UINT16_MAX)
3767 pThis->offFat32InfoSector = UINT64_MAX;
3768 else if (pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo >= pThis->cReservedSectors)
3769 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3770 "Bogus FAT32 info sector number: %#x (reserved sectors %#x)",
3771 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pThis->cReservedSectors);
3772 else
3773 {
3774 pThis->offFat32InfoSector = pThis->cbSector * pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo + pThis->offBootSector;
3775 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->offFat32InfoSector,
3776 &pThis->Fat32InfoSector, sizeof(pThis->Fat32InfoSector), NULL);
3777 if (RT_FAILURE(rc))
3778 return RTErrInfoSetF(pErrInfo, rc, "Failed to read FAT32 info sector at offset %#RX64", pThis->offFat32InfoSector);
3779 if ( pThis->Fat32InfoSector.uSignature1 != FAT32INFOSECTOR_SIGNATURE_1
3780 || pThis->Fat32InfoSector.uSignature2 != FAT32INFOSECTOR_SIGNATURE_2
3781 || pThis->Fat32InfoSector.uSignature3 != FAT32INFOSECTOR_SIGNATURE_3)
3782 return RTErrInfoSetF(pErrInfo, rc, "FAT32 info sector signature mismatch: %#x %#x %#x",
3783 pThis->Fat32InfoSector.uSignature1, pThis->Fat32InfoSector.uSignature2,
3784 pThis->Fat32InfoSector.uSignature3);
3785 }
3786
3787 /*
3788 * Boot sector copy.
3789 */
3790 if ( pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == 0
3791 || pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == UINT16_MAX)
3792 {
3793 pThis->cBootSectorCopies = 0;
3794 pThis->offBootSectorCopies = UINT64_MAX;
3795 }
3796 else if (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo >= pThis->cReservedSectors)
3797 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3798 "Bogus FAT32 info boot sector copy location: %#x (reserved sectors %#x)",
3799 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo, pThis->cReservedSectors);
3800 else
3801 {
3802 /** @todo not sure if cbSector is correct here. */
3803 pThis->cBootSectorCopies = 3;
3804 if ( (uint32_t)pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo + pThis->cBootSectorCopies
3805 > pThis->cReservedSectors)
3806 pThis->cBootSectorCopies = (uint8_t)(pThis->cReservedSectors - pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
3807 pThis->offBootSectorCopies = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo * pThis->cbSector + pThis->offBootSector;
3808 if ( pThis->offFat32InfoSector != UINT64_MAX
3809 && pThis->offFat32InfoSector - pThis->offBootSectorCopies < (uint64_t)(pThis->cBootSectorCopies * pThis->cbSector))
3810 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 info sector and boot sector copies overlap: %#x vs %#x",
3811 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
3812 }
3813
3814 /*
3815 * Serial number, label and type.
3816 */
3817 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Fat32Ebpb.bExtSignature, pBootSector->Bpb.Fat32Ebpb.uSerialNumber,
3818 pBootSector->Bpb.Fat32Ebpb.achLabel,
3819 fUsing64BitTotalSectorCount ? pBootSector->achOemName : pBootSector->Bpb.Fat32Ebpb.achLabel);
3820 if (pThis->szType[0] == '\0')
3821 memcpy(pThis->szType, "FAT32", 6);
3822
3823 return VINF_SUCCESS;
3824}
3825
3826
3827/**
3828 * Tries to detect a DOS 2.0+ formatted image and fills in the BPB fields.
3829 *
3830 * We ASSUME BPB here, but need to figure out which version of the BPB it is,
3831 * which is lots of fun.
3832 *
3833 * @returns IPRT status code.
3834 * @param pThis The FAT volume instance, BPB derived fields are filled
3835 * in on success.
3836 * @param pBootSector The boot sector.
3837 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
3838 * the boot sector. On successful return it will contain
3839 * the first FAT sector.
3840 * @param pErrInfo Where to return additional error information.
3841 */
3842static int rtFsFatVolTryInitDos2Plus(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t *pbFatSector, PRTERRINFO pErrInfo)
3843{
3844 /*
3845 * Check if we've got a known jump instruction first, because that will
3846 * give us a max (E)BPB size hint.
3847 */
3848 uint8_t offJmp = UINT8_MAX;
3849 if ( pBootSector->abJmp[0] == 0xeb
3850 && pBootSector->abJmp[1] <= 0x7f)
3851 offJmp = pBootSector->abJmp[1] + 2;
3852 else if ( pBootSector->abJmp[0] == 0x90
3853 && pBootSector->abJmp[1] == 0xeb
3854 && pBootSector->abJmp[2] <= 0x7f)
3855 offJmp = pBootSector->abJmp[2] + 3;
3856 else if ( pBootSector->abJmp[0] == 0xe9
3857 && pBootSector->abJmp[2] <= 0x7f)
3858 offJmp = RT_MIN(127, RT_MAKE_U16(pBootSector->abJmp[1], pBootSector->abJmp[2]));
3859 uint8_t const cbMaxBpb = offJmp - RT_OFFSETOF(FATBOOTSECTOR, Bpb);
3860
3861 /*
3862 * Do the basic DOS v2.0 BPB fields.
3863 */
3864 if (cbMaxBpb < sizeof(FATBPB20))
3865 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3866 "DOS signature, but jmp too short for any BPB: %#x (max %#x BPB)", offJmp, cbMaxBpb);
3867
3868 if (pBootSector->Bpb.Bpb20.cFats == 0)
3869 return RTErrInfoSet(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, number of FATs is zero, so not FAT file system");
3870 if (pBootSector->Bpb.Bpb20.cFats > 4)
3871 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many FATs: %#x", pBootSector->Bpb.Bpb20.cFats);
3872 pThis->cFats = pBootSector->Bpb.Bpb20.cFats;
3873
3874 if (!FATBPB_MEDIA_IS_VALID(pBootSector->Bpb.Bpb20.bMedia))
3875 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3876 "DOS signature, invalid media byte: %#x", pBootSector->Bpb.Bpb20.bMedia);
3877 pThis->bMedia = pBootSector->Bpb.Bpb20.bMedia;
3878
3879 if (!RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cbSector))
3880 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3881 "DOS signature, sector size not power of two: %#x", pBootSector->Bpb.Bpb20.cbSector);
3882 if ( pBootSector->Bpb.Bpb20.cbSector != 512
3883 && pBootSector->Bpb.Bpb20.cbSector != 4096
3884 && pBootSector->Bpb.Bpb20.cbSector != 1024
3885 && pBootSector->Bpb.Bpb20.cbSector != 128)
3886 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3887 "DOS signature, unsupported sector size: %#x", pBootSector->Bpb.Bpb20.cbSector);
3888 pThis->cbSector = pBootSector->Bpb.Bpb20.cbSector;
3889
3890 if ( !RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cSectorsPerCluster)
3891 || !pBootSector->Bpb.Bpb20.cSectorsPerCluster)
3892 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, cluster size not non-zero power of two: %#x",
3893 pBootSector->Bpb.Bpb20.cSectorsPerCluster);
3894 pThis->cbCluster = pBootSector->Bpb.Bpb20.cSectorsPerCluster * pThis->cbSector;
3895
3896 uint64_t const cMaxRoot = (pThis->cbBacking - pThis->offBootSector - 512) / sizeof(FATDIRENTRY); /* we'll check again later. */
3897 if (pBootSector->Bpb.Bpb20.cMaxRootDirEntries >= cMaxRoot)
3898 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many root entries: %#x (max %#RX64)",
3899 pBootSector->Bpb.Bpb20.cSectorsPerCluster, cMaxRoot);
3900 pThis->cRootDirEntries = pBootSector->Bpb.Bpb20.cMaxRootDirEntries;
3901
3902 if ( pBootSector->Bpb.Bpb20.cReservedSectors == 0
3903 || pBootSector->Bpb.Bpb20.cReservedSectors >= _32K)
3904 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3905 "DOS signature, bogus reserved sector count: %#x", pBootSector->Bpb.Bpb20.cReservedSectors);
3906 pThis->cReservedSectors = pBootSector->Bpb.Bpb20.cReservedSectors;
3907 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * pThis->cbSector;
3908
3909 /*
3910 * Jump ahead and check for FAT32 EBPB.
3911 * If found, we simply ASSUME it's a FAT32 file system.
3912 */
3913 int rc;
3914 if ( ( sizeof(FAT32EBPB) <= cbMaxBpb
3915 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE)
3916 || ( RT_OFFSETOF(FAT32EBPB, achLabel) <= cbMaxBpb
3917 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
3918 {
3919 rc = rtFsFatVolTryInitDos2PlusFat32(pThis, pBootSector, pErrInfo);
3920 if (RT_FAILURE(rc))
3921 return rc;
3922 }
3923 else
3924 {
3925 /*
3926 * Check for extended BPB, otherwise we'll have to make qualified guesses
3927 * about what kind of BPB we're up against based on jmp offset and zero fields.
3928 * ASSUMES either FAT16 or FAT12.
3929 */
3930 if ( ( sizeof(FATEBPB) <= cbMaxBpb
3931 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE)
3932 || ( RT_OFFSETOF(FATEBPB, achLabel) <= cbMaxBpb
3933 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
3934 {
3935 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Ebpb.bExtSignature, pBootSector->Bpb.Ebpb.uSerialNumber,
3936 pBootSector->Bpb.Ebpb.achLabel, pBootSector->Bpb.Ebpb.achType);
3937 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, true /*fMaybe331*/, pErrInfo);
3938 pThis->enmBpbVersion = pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE
3939 ? RTFSFATBPBVER_EXT_29 : RTFSFATBPBVER_EXT_28;
3940 }
3941 else
3942 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, cbMaxBpb >= sizeof(FATBPB331), pErrInfo);
3943 if (RT_FAILURE(rc))
3944 return rc;
3945 if (pThis->szType[0] == '\0')
3946 memcpy(pThis->szType, pThis->enmFatType == RTFSFATTYPE_FAT12 ? "FAT12" : "FAT16", 6);
3947 }
3948
3949 /*
3950 * Check the FAT ID. May have to read a bit of the FAT into the buffer.
3951 */
3952 if (pThis->aoffFats[0] != pThis->offBootSector + 512)
3953 {
3954 rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0], pbFatSector, 512, NULL);
3955 if (RT_FAILURE(rc))
3956 return RTErrInfoSet(pErrInfo, rc, "error reading first FAT sector");
3957 }
3958 if (pbFatSector[0] != pThis->bMedia)
3959 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3960 "Media byte and FAT ID mismatch: %#x vs %#x (%.7Rhxs)", pbFatSector[0], pThis->bMedia, pbFatSector);
3961 switch (pThis->enmFatType)
3962 {
3963 case RTFSFATTYPE_FAT12:
3964 if ((pbFatSector[1] & 0xf) != 0xf)
3965 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT12): %.3Rhxs", pbFatSector);
3966 pThis->idxMaxLastCluster = FAT_LAST_FAT12_DATA_CLUSTER;
3967 pThis->idxEndOfChain = (pbFatSector[1] >> 4) | ((uint32_t)pbFatSector[2] << 4);
3968 break;
3969
3970 case RTFSFATTYPE_FAT16:
3971 if (pbFatSector[1] != 0xff)
3972 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT16): %.4Rhxs", pbFatSector);
3973 pThis->idxMaxLastCluster = FAT_LAST_FAT16_DATA_CLUSTER;
3974 pThis->idxEndOfChain = RT_MAKE_U16(pbFatSector[2], pbFatSector[3]);
3975 break;
3976
3977 case RTFSFATTYPE_FAT32:
3978 if ( pbFatSector[1] != 0xff
3979 || pbFatSector[2] != 0xff
3980 || (pbFatSector[3] & 0x0f) != 0x0f)
3981 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT32): %.8Rhxs", pbFatSector);
3982 pThis->idxMaxLastCluster = FAT_LAST_FAT32_DATA_CLUSTER;
3983 pThis->idxEndOfChain = RT_MAKE_U32_FROM_U8(pbFatSector[4], pbFatSector[5], pbFatSector[6], pbFatSector[7]);
3984 break;
3985
3986 default: AssertFailedReturn(VERR_INTERNAL_ERROR_2);
3987 }
3988 if (pThis->idxEndOfChain <= pThis->idxMaxLastCluster)
3989 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus formatter end-of-chain value: %#x, must be above %#x",
3990 pThis->idxEndOfChain, pThis->idxMaxLastCluster);
3991
3992 RT_NOREF(pbFatSector);
3993 return VINF_SUCCESS;
3994}
3995
3996
3997/**
3998 * Given a power of two value @a cb return exponent value.
3999 *
4000 * @returns Shift count
4001 * @param cb The value.
4002 */
4003static uint8_t rtFsFatVolCalcByteShiftCount(uint32_t cb)
4004{
4005 Assert(RT_IS_POWER_OF_TWO(cb));
4006 unsigned iBit = ASMBitFirstSetU32(cb);
4007 Assert(iBit >= 1);
4008 iBit--;
4009 return iBit;
4010}
4011
4012
4013/**
4014 * Worker for RTFsFatVolOpen.
4015 *
4016 * @returns IPRT status code.
4017 * @param pThis The FAT VFS instance to initialize.
4018 * @param hVfsSelf The FAT VFS handle (no reference consumed).
4019 * @param hVfsBacking The file backing the alleged FAT file system.
4020 * Reference is consumed (via rtFsFatVol_Destroy).
4021 * @param fReadOnly Readonly or readwrite mount.
4022 * @param offBootSector The boot sector offset in bytes.
4023 * @param pErrInfo Where to return additional error info. Can be NULL.
4024 */
4025static int rtFsFatVolTryInit(PRTFSFATVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking,
4026 bool fReadOnly, uint64_t offBootSector, PRTERRINFO pErrInfo)
4027{
4028 /*
4029 * First initialize the state so that rtFsFatVol_Destroy won't trip up.
4030 */
4031 pThis->hVfsSelf = hVfsSelf;
4032 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsFatVol_Destroy releases it. */
4033 pThis->cbBacking = 0;
4034 pThis->offBootSector = offBootSector;
4035 pThis->offNanoUTC = RTTimeLocalDeltaNano();
4036 pThis->offMinUTC = pThis->offNanoUTC / RT_NS_1MIN;
4037 pThis->fReadOnly = fReadOnly;
4038 pThis->cReservedSectors = 1;
4039
4040 pThis->cbSector = 512;
4041 pThis->cbCluster = 512;
4042 pThis->cClusters = 0;
4043 pThis->offFirstCluster = 0;
4044 pThis->cbTotalSize = 0;
4045
4046 pThis->enmFatType = RTFSFATTYPE_INVALID;
4047 pThis->cFatEntries = 0;
4048 pThis->cFats = 0;
4049 pThis->cbFat = 0;
4050 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aoffFats); i++)
4051 pThis->aoffFats[i] = UINT64_MAX;
4052 pThis->pFatCache = NULL;
4053
4054 pThis->offRootDir = UINT64_MAX;
4055 pThis->idxRootDirCluster = UINT32_MAX;
4056 pThis->cRootDirEntries = UINT32_MAX;
4057 pThis->cbRootDir = 0;
4058 pThis->hVfsRootDir = NIL_RTVFSDIR;
4059 pThis->pRootDir = NULL;
4060
4061 pThis->uSerialNo = 0;
4062 pThis->szLabel[0] = '\0';
4063 pThis->szType[0] = '\0';
4064 pThis->cBootSectorCopies = 0;
4065 pThis->fFat32Flags = 0;
4066 pThis->offBootSectorCopies = UINT64_MAX;
4067 pThis->offFat32InfoSector = UINT64_MAX;
4068 RT_ZERO(pThis->Fat32InfoSector);
4069
4070 /*
4071 * Get stuff that may fail.
4072 */
4073 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
4074 if (RT_FAILURE(rc))
4075 return rc;
4076 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
4077
4078 /*
4079 * Read the boot sector and the following sector (start of the allocation
4080 * table unless it a FAT32 FS). We'll then validate the boot sector and
4081 * start of the FAT, expanding the BPB into the instance data.
4082 */
4083 union
4084 {
4085 uint8_t ab[512*2];
4086 uint16_t au16[512*2 / 2];
4087 uint32_t au32[512*2 / 4];
4088 FATBOOTSECTOR BootSector;
4089 FAT32INFOSECTOR InfoSector;
4090 } Buf;
4091 RT_ZERO(Buf);
4092
4093 rc = RTVfsFileReadAt(hVfsBacking, offBootSector, &Buf.BootSector, 512 * 2, NULL);
4094 if (RT_FAILURE(rc))
4095 return RTErrInfoSet(pErrInfo, rc, "Unable to read bootsect");
4096
4097 /*
4098 * Extract info from the BPB and validate the two special FAT entries.
4099 *
4100 * Check the DOS signature first. The PC-DOS 1.0 boot floppy does not have
4101 * a signature and we ASSUME this is the case for all flopies formated by it.
4102 */
4103 if (Buf.BootSector.uSignature != FATBOOTSECTOR_SIGNATURE)
4104 {
4105 if (Buf.BootSector.uSignature != 0)
4106 return RTErrInfoSetF(pErrInfo, rc, "No DOS bootsector signature: %#06x", Buf.BootSector.uSignature);
4107 rc = rtFsFatVolTryInitDos1x(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
4108 }
4109 else
4110 rc = rtFsFatVolTryInitDos2Plus(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
4111 if (RT_FAILURE(rc))
4112 return rc;
4113
4114 /*
4115 * Calc shift counts.
4116 */
4117 pThis->cSectorByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbSector);
4118 pThis->cClusterByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbCluster);
4119
4120 /*
4121 * Setup the FAT cache.
4122 */
4123 rc = rtFsFatClusterMap_Create(pThis, &Buf.ab[512], pErrInfo);
4124 if (RT_FAILURE(rc))
4125 return rc;
4126
4127 /*
4128 * Create the root directory fun.
4129 */
4130 if (pThis->idxRootDirCluster == UINT32_MAX)
4131 rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
4132 UINT32_MAX, pThis->offRootDir, pThis->cbRootDir,
4133 &pThis->hVfsRootDir, &pThis->pRootDir);
4134 else
4135 rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
4136 pThis->idxRootDirCluster, UINT64_MAX, pThis->cbRootDir,
4137 &pThis->hVfsRootDir, &pThis->pRootDir);
4138 return rc;
4139}
4140
4141
4142/**
4143 * Opens a FAT file system volume.
4144 *
4145 * @returns IPRT status code.
4146 * @param hVfsFileIn The file or device backing the volume.
4147 * @param fReadOnly Whether to mount it read-only.
4148 * @param offBootSector The offset of the boot sector relative to the start
4149 * of @a hVfsFileIn. Pass 0 for floppies.
4150 * @param phVfs Where to return the virtual file system handle.
4151 * @param pErrInfo Where to return additional error information.
4152 */
4153RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, uint64_t offBootSector, PRTVFS phVfs, PRTERRINFO pErrInfo)
4154{
4155 /*
4156 * Quick input validation.
4157 */
4158 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
4159 *phVfs = NIL_RTVFS;
4160
4161 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
4162 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
4163
4164 /*
4165 * Create a new FAT VFS instance and try initialize it using the given input file.
4166 */
4167 RTVFS hVfs = NIL_RTVFS;
4168 void *pvThis = NULL;
4169 int rc = RTVfsNew(&g_rtFsFatVolOps, sizeof(RTFSFATVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
4170 if (RT_SUCCESS(rc))
4171 {
4172 rc = rtFsFatVolTryInit((PRTFSFATVOL)pvThis, hVfs, hVfsFileIn, fReadOnly, offBootSector, pErrInfo);
4173 if (RT_SUCCESS(rc))
4174 *phVfs = hVfs;
4175 else
4176 RTVfsRelease(hVfs);
4177 }
4178 else
4179 RTVfsFileRelease(hVfsFileIn);
4180 return rc;
4181}
4182
4183
4184
4185
4186/**
4187 * Fills a range in the file with zeros in the most efficient manner.
4188 *
4189 * @returns IPRT status code.
4190 * @param hVfsFile The file to write to.
4191 * @param off Where to start filling with zeros.
4192 * @param cbZeros How many zero blocks to write.
4193 */
4194static int rtFsFatVolWriteZeros(RTVFSFILE hVfsFile, uint64_t off, uint32_t cbZeros)
4195{
4196 while (cbZeros > 0)
4197 {
4198 uint32_t cbToWrite = sizeof(g_abRTZero64K);
4199 if (cbToWrite > cbZeros)
4200 cbToWrite = cbZeros;
4201 int rc = RTVfsFileWriteAt(hVfsFile, off, g_abRTZero64K, cbToWrite, NULL);
4202 if (RT_FAILURE(rc))
4203 return rc;
4204 off += cbToWrite;
4205 cbZeros -= cbToWrite;
4206 }
4207 return VINF_SUCCESS;
4208}
4209
4210
4211/**
4212 * Formats a FAT volume.
4213 *
4214 * @returns IRPT status code.
4215 * @param hVfsFile The volume file.
4216 * @param offVol The offset into @a hVfsFile of the file.
4217 * Typically 0.
4218 * @param cbVol The size of the volume. Pass 0 if the rest of
4219 * hVfsFile should be used.
4220 * @param fFlags See RTFSFATVOL_FMT_F_XXX.
4221 * @param cbSector The logical sector size. Must be power of two.
4222 * Optional, pass zero to use 512.
4223 * @param cSectorsPerCluster Number of sectors per cluster. Power of two.
4224 * Optional, pass zero to auto detect.
4225 * @param enmFatType The FAT type (12, 16, 32) to use.
4226 * Optional, pass RTFSFATTYPE_INVALID for default.
4227 * @param cHeads The number of heads to report in the BPB.
4228 * Optional, pass zero to auto detect.
4229 * @param cSectorsPerTrack The number of sectors per track to put in the
4230 * BPB. Optional, pass zero to auto detect.
4231 * @param bMedia The media byte value and FAT ID to use.
4232 * Optional, pass zero to auto detect.
4233 * @param cRootDirEntries Number of root directory entries.
4234 * Optional, pass zero to auto detect.
4235 * @param cHiddenSectors Number of hidden sectors. Pass 0 for
4236 * unpartitioned media.
4237 * @param pErrInfo Additional error information, maybe. Optional.
4238 */
4239RTDECL(int) RTFsFatVolFormat(RTVFSFILE hVfsFile, uint64_t offVol, uint64_t cbVol, uint32_t fFlags, uint16_t cbSector,
4240 uint16_t cSectorsPerCluster, RTFSFATTYPE enmFatType, uint32_t cHeads, uint32_t cSectorsPerTrack,
4241 uint8_t bMedia, uint16_t cRootDirEntries, uint32_t cHiddenSectors, PRTERRINFO pErrInfo)
4242{
4243 int rc;
4244 uint32_t cFats = 2;
4245
4246 /*
4247 * Validate input.
4248 */
4249 if (!cbSector)
4250 cbSector = 512;
4251 else
4252 AssertMsgReturn( cbSector == 128
4253 || cbSector == 512
4254 || cbSector == 1024
4255 || cbSector == 4096,
4256 ("cbSector=%#x\n", cbSector),
4257 VERR_INVALID_PARAMETER);
4258 AssertMsgReturn(cSectorsPerCluster == 0 || (cSectorsPerCluster <= 128 && RT_IS_POWER_OF_TWO(cSectorsPerCluster)),
4259 ("cSectorsPerCluster=%#x\n", cSectorsPerCluster), VERR_INVALID_PARAMETER);
4260 if (bMedia != 0)
4261 {
4262 AssertMsgReturn(FAT_ID_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
4263 AssertMsgReturn(FATBPB_MEDIA_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
4264 }
4265 AssertReturn(!(fFlags & ~RTFSFATVOL_FMT_F_VALID_MASK), VERR_INVALID_FLAGS);
4266 AssertReturn(enmFatType >= RTFSFATTYPE_INVALID && enmFatType < RTFSFATTYPE_END, VERR_INVALID_PARAMETER);
4267
4268 if (!cbVol)
4269 {
4270 uint64_t cbFile;
4271 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
4272 AssertRCReturn(rc, rc);
4273 AssertMsgReturn(cbFile > offVol, ("cbFile=%#RX64 offVol=%#RX64\n", cbFile, offVol), VERR_INVALID_PARAMETER);
4274 cbVol = cbFile - offVol;
4275 }
4276 uint64_t const cSectorsInVol = cbVol / cbSector;
4277
4278 /*
4279 * Guess defaults if necessary.
4280 */
4281 if (!cSectorsPerCluster || !cHeads || !cSectorsPerTrack || !bMedia || !cRootDirEntries)
4282 {
4283 static struct
4284 {
4285 uint64_t cbVol;
4286 uint8_t bMedia;
4287 uint8_t cHeads;
4288 uint8_t cSectorsPerTrack;
4289 uint8_t cSectorsPerCluster;
4290 uint16_t cRootDirEntries;
4291 } s_aDefaults[] =
4292 {
4293 /* cbVol, bMedia, cHeads, cSectorsPTrk, cSectorsPClstr, cRootDirEntries */
4294 { 163840, 0xfe, /* cyl: 40,*/ 1, 8, 1, 64 },
4295 { 184320, 0xfc, /* cyl: 40,*/ 1, 9, 2, 64 },
4296 { 327680, 0xff, /* cyl: 40,*/ 2, 8, 2, 112 },
4297 { 368640, 0xfd, /* cyl: 40,*/ 2, 9, 2, 112 },
4298 { 737280, 0xf9, /* cyl: 80,*/ 2, 9, 2, 112 },
4299 { 1228800, 0xf9, /* cyl: 80,*/ 2, 15, 2, 112 },
4300 { 1474560, 0xf0, /* cyl: 80,*/ 2, 18, 1, 224 },
4301 { 2949120, 0xf0, /* cyl: 80,*/ 2, 36, 2, 224 },
4302 { 528482304, 0xf8, /* cyl: 1024,*/ 16, 63, 0, 512 }, // 504MB limit
4303 { UINT64_C(7927234560), 0xf8, /* cyl: 1024,*/ 240, 63, 0, 512 }, // 7.3GB limit
4304 { UINT64_C(8422686720), 0xf8, /* cyl: 1024,*/ 255, 63, 0, 512 }, // 7.84GB limit
4305
4306 };
4307 uint32_t iDefault = 0;
4308 while ( iDefault < RT_ELEMENTS(s_aDefaults) - 1U
4309 && cbVol > s_aDefaults[iDefault].cbVol)
4310 iDefault++;
4311 if (!cHeads)
4312 cHeads = s_aDefaults[iDefault].cHeads;
4313 if (!cSectorsPerTrack)
4314 cSectorsPerTrack = s_aDefaults[iDefault].cSectorsPerTrack;
4315 if (!bMedia)
4316 bMedia = s_aDefaults[iDefault].bMedia;
4317 if (!cRootDirEntries)
4318 cRootDirEntries = s_aDefaults[iDefault].cRootDirEntries;
4319 if (!cSectorsPerCluster)
4320 {
4321 cSectorsPerCluster = s_aDefaults[iDefault].cSectorsPerCluster;
4322 if (!cSectorsPerCluster)
4323 {
4324 uint32_t cbFat12Overhead = cbSector /* boot sector */
4325 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
4326 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4327 uint32_t cbFat16Overhead = cbSector /* boot sector */
4328 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
4329 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4330
4331 if ( enmFatType == RTFSFATTYPE_FAT12
4332 || cbVol <= cbFat12Overhead + FAT_MAX_FAT12_DATA_CLUSTERS * 4 * cbSector)
4333 {
4334 enmFatType = RTFSFATTYPE_FAT12;
4335 cSectorsPerCluster = 1;
4336 while ( cSectorsPerCluster < 128
4337 && cSectorsInVol
4338 > cbFat12Overhead / cbSector
4339 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT12_DATA_CLUSTERS
4340 + cSectorsPerCluster - 1)
4341 cSectorsPerCluster <<= 1;
4342 }
4343 else if ( enmFatType == RTFSFATTYPE_FAT16
4344 || cbVol <= cbFat16Overhead + FAT_MAX_FAT16_DATA_CLUSTERS * 128 * cbSector)
4345 {
4346 enmFatType = RTFSFATTYPE_FAT16;
4347 cSectorsPerCluster = 1;
4348 while ( cSectorsPerCluster < 128
4349 && cSectorsInVol
4350 > cbFat12Overhead / cbSector
4351 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT16_DATA_CLUSTERS
4352 + cSectorsPerCluster - 1)
4353 cSectorsPerCluster <<= 1;
4354 }
4355 else
4356 {
4357 /* The target here is keeping the FAT size below 8MB. Seems windows
4358 likes a minimum 4KB cluster size as wells as a max of 32KB (googling). */
4359 enmFatType = RTFSFATTYPE_FAT32;
4360 uint32_t cbFat32Overhead = cbSector * 32 /* boot sector, info sector, boot sector copies, reserved sectors */
4361 + _8M * cFats;
4362 if (cbSector >= _4K)
4363 cSectorsPerCluster = 1;
4364 else
4365 cSectorsPerCluster = _4K / cbSector;
4366 while ( cSectorsPerCluster < 128
4367 && cSectorsPerCluster * cbSector < _32K
4368 && cSectorsInVol > cbFat32Overhead / cbSector + (uint64_t)cSectorsPerCluster * _2M)
4369 cSectorsPerCluster <<= 1;
4370 }
4371 }
4372 }
4373 }
4374 Assert(cSectorsPerCluster);
4375 Assert(cRootDirEntries);
4376 uint32_t cbRootDir = RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector);
4377 uint32_t const cbCluster = cSectorsPerCluster * cbSector;
4378
4379 /*
4380 * If we haven't figured out the FAT type yet, do so.
4381 * The file system code determins the FAT based on cluster counts,
4382 * so we must do so here too.
4383 */
4384 if (enmFatType == RTFSFATTYPE_INVALID)
4385 {
4386 uint32_t cbFat12Overhead = cbSector /* boot sector */
4387 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
4388 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4389 if ( cbVol <= cbFat12Overhead + cbCluster
4390 || (cbVol - cbFat12Overhead) / cbCluster <= FAT_MAX_FAT12_DATA_CLUSTERS)
4391 enmFatType = RTFSFATTYPE_FAT12;
4392 else
4393 {
4394 uint32_t cbFat16Overhead = cbSector /* boot sector */
4395 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
4396 + cbRootDir;
4397 if ( cbVol <= cbFat16Overhead + cbCluster
4398 || (cbVol - cbFat16Overhead) / cbCluster <= FAT_MAX_FAT16_DATA_CLUSTERS)
4399 enmFatType = RTFSFATTYPE_FAT16;
4400 else
4401 enmFatType = RTFSFATTYPE_FAT32;
4402 }
4403 }
4404 if (enmFatType == RTFSFATTYPE_FAT32)
4405 cbRootDir = cbCluster;
4406
4407 /*
4408 * Calculate the FAT size and number of data cluster.
4409 *
4410 * Since the FAT size depends on how many data clusters there are, we start
4411 * with a minimum FAT size and maximum clust count, then recalucate it. The
4412 * result isn't necessarily stable, so we will only retry stabalizing the
4413 * result a few times.
4414 */
4415 uint32_t cbReservedFixed = enmFatType == RTFSFATTYPE_FAT32 ? 32 * cbSector : cbSector + cbRootDir;
4416 uint32_t cbFat = cbSector;
4417 if (cbReservedFixed + cbFat * cFats >= cbVol)
4418 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
4419 cbVol, cbReservedFixed, cbFat, cFats);
4420 uint32_t cMaxClusters = enmFatType == RTFSFATTYPE_FAT12 ? FAT_MAX_FAT12_DATA_CLUSTERS
4421 : enmFatType == RTFSFATTYPE_FAT16 ? FAT_MAX_FAT16_DATA_CLUSTERS
4422 : FAT_MAX_FAT12_DATA_CLUSTERS;
4423 uint32_t cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
4424 uint32_t cPrevClusters;
4425 uint32_t cTries = 4;
4426 do
4427 {
4428 cPrevClusters = cClusters;
4429 switch (enmFatType)
4430 {
4431 case RTFSFATTYPE_FAT12:
4432 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT12_TOTAL_CLUSTERS, cClusters) * 3 / 2;
4433 break;
4434 case RTFSFATTYPE_FAT16:
4435 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT16_TOTAL_CLUSTERS, cClusters) * 2;
4436 break;
4437 case RTFSFATTYPE_FAT32:
4438 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT32_TOTAL_CLUSTERS, cClusters) * 4;
4439 cbFat = RT_ALIGN_32(cbFat, _4K);
4440 break;
4441 default:
4442 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4443 }
4444 cbFat = RT_ALIGN_32(cbFat, cbSector);
4445 if (cbReservedFixed + cbFat * cFats >= cbVol)
4446 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
4447 cbVol, cbReservedFixed, cbFat, cFats);
4448 cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
4449 } while ( cClusters != cPrevClusters
4450 && cTries-- > 0);
4451 uint64_t const cTotalSectors = cClusters * (uint64_t)cSectorsPerCluster + (cbReservedFixed + cbFat * cFats) / cbSector;
4452
4453 /*
4454 * Check that the file system type and cluster count matches up. If they
4455 * don't the type will be misdetected.
4456 *
4457 * Note! These assertions could trigger if the above calculations are wrong.
4458 */
4459 switch (enmFatType)
4460 {
4461 case RTFSFATTYPE_FAT12:
4462 AssertMsgReturn(cClusters >= FAT_MIN_FAT12_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT12_DATA_CLUSTERS,
4463 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4464 break;
4465 case RTFSFATTYPE_FAT16:
4466 AssertMsgReturn(cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT16_DATA_CLUSTERS,
4467 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4468 break;
4469 case RTFSFATTYPE_FAT32:
4470 AssertMsgReturn(cClusters >= FAT_MIN_FAT32_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS,
4471 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4472 default:
4473 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4474 }
4475
4476 /*
4477 * Okay, create the boot sector.
4478 */
4479 size_t cbBuf = RT_MAX(RT_MAX(_64K, cbCluster), cbSector * 2U);
4480 uint8_t *pbBuf = (uint8_t *)RTMemTmpAllocZ(cbBuf);
4481 AssertReturn(pbBuf, VERR_NO_TMP_MEMORY);
4482
4483 const char *pszLastOp = "boot sector";
4484 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pbBuf;
4485 pBootSector->abJmp[0] = 0xeb;
4486 pBootSector->abJmp[1] = RT_UOFFSETOF(FATBOOTSECTOR, Bpb)
4487 + (enmFatType == RTFSFATTYPE_FAT32 ? sizeof(FAT32EBPB) : sizeof(FATEBPB)) - 2;
4488 pBootSector->abJmp[2] = 0x90;
4489 memcpy(pBootSector->achOemName, enmFatType == RTFSFATTYPE_FAT32 ? "FAT32 " : "IPRT 6.2", sizeof(pBootSector->achOemName));
4490 pBootSector->Bpb.Bpb331.cbSector = (uint16_t)cbSector;
4491 pBootSector->Bpb.Bpb331.cSectorsPerCluster = (uint8_t)cSectorsPerCluster;
4492 pBootSector->Bpb.Bpb331.cReservedSectors = enmFatType == RTFSFATTYPE_FAT32 ? cbReservedFixed / cbSector : 1;
4493 pBootSector->Bpb.Bpb331.cFats = (uint8_t)cFats;
4494 pBootSector->Bpb.Bpb331.cMaxRootDirEntries = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cRootDirEntries;
4495 pBootSector->Bpb.Bpb331.cTotalSectors16 = cTotalSectors <= UINT16_MAX ? (uint16_t)cTotalSectors : 0;
4496 pBootSector->Bpb.Bpb331.bMedia = bMedia;
4497 pBootSector->Bpb.Bpb331.cSectorsPerFat = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cbFat / cbSector;
4498 pBootSector->Bpb.Bpb331.cSectorsPerTrack = cSectorsPerTrack;
4499 pBootSector->Bpb.Bpb331.cTracksPerCylinder = cHeads;
4500 pBootSector->Bpb.Bpb331.cHiddenSectors = cHiddenSectors;
4501 pBootSector->Bpb.Bpb331.cTotalSectors32 = cTotalSectors <= UINT32_MAX ? (uint32_t)cTotalSectors : 0;
4502 if (enmFatType != RTFSFATTYPE_FAT32)
4503 {
4504 pBootSector->Bpb.Ebpb.bInt13Drive = 0;
4505 pBootSector->Bpb.Ebpb.bReserved = 0;
4506 pBootSector->Bpb.Ebpb.bExtSignature = FATEBPB_SIGNATURE;
4507 pBootSector->Bpb.Ebpb.uSerialNumber = RTRandU32();
4508 memset(pBootSector->Bpb.Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Ebpb.achLabel));
4509 memcpy(pBootSector->Bpb.Ebpb.achType, enmFatType == RTFSFATTYPE_FAT12 ? "FAT12 " : "FAT16 ",
4510 sizeof(pBootSector->Bpb.Ebpb.achType));
4511 }
4512 else
4513 {
4514 pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 = cbFat / cbSector;
4515 pBootSector->Bpb.Fat32Ebpb.fFlags = 0;
4516 pBootSector->Bpb.Fat32Ebpb.uVersion = FAT32EBPB_VERSION_0_0;
4517 pBootSector->Bpb.Fat32Ebpb.uRootDirCluster = FAT_FIRST_DATA_CLUSTER;
4518 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo = 1;
4519 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo = 6;
4520 RT_ZERO(pBootSector->Bpb.Fat32Ebpb.abReserved);
4521
4522 pBootSector->Bpb.Fat32Ebpb.bInt13Drive = 0;
4523 pBootSector->Bpb.Fat32Ebpb.bReserved = 0;
4524 pBootSector->Bpb.Fat32Ebpb.bExtSignature = FATEBPB_SIGNATURE;
4525 pBootSector->Bpb.Fat32Ebpb.uSerialNumber = RTRandU32();
4526 memset(pBootSector->Bpb.Fat32Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Fat32Ebpb.achLabel));
4527 if (cTotalSectors > UINT32_MAX)
4528 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 = cTotalSectors;
4529 else
4530 memcpy(pBootSector->Bpb.Fat32Ebpb.u.achType, "FAT32 ", sizeof(pBootSector->Bpb.Fat32Ebpb.u.achType));
4531 }
4532 pbBuf[pBootSector->abJmp[1] + 2 + 0] = 0xcd; /* int 19h */
4533 pbBuf[pBootSector->abJmp[1] + 2 + 1] = 0x19;
4534 pbBuf[pBootSector->abJmp[1] + 2 + 2] = 0xcc; /* int3 */
4535 pbBuf[pBootSector->abJmp[1] + 2 + 3] = 0xcc;
4536
4537 pBootSector->uSignature = FATBOOTSECTOR_SIGNATURE;
4538 if (cbSector != sizeof(*pBootSector))
4539 *(uint16_t *)&pbBuf[cbSector - 2] = FATBOOTSECTOR_SIGNATURE; /** @todo figure out how disks with non-512 byte sectors work! */
4540
4541 rc = RTVfsFileWriteAt(hVfsFile, offVol, pBootSector, cbSector, NULL);
4542 uint32_t const offFirstFat = pBootSector->Bpb.Bpb331.cReservedSectors * cbSector;
4543
4544 /*
4545 * Write the FAT32 info sector, 3 boot sector copies, and zero fill
4546 * the other reserved sectors.
4547 */
4548 if (RT_SUCCESS(rc) && enmFatType == RTFSFATTYPE_FAT32)
4549 {
4550 pszLastOp = "fat32 info sector";
4551 PFAT32INFOSECTOR pInfoSector = (PFAT32INFOSECTOR)&pbBuf[cbSector]; /* preserve the boot sector. */
4552 RT_ZERO(*pInfoSector);
4553 pInfoSector->uSignature1 = FAT32INFOSECTOR_SIGNATURE_1;
4554 pInfoSector->uSignature2 = FAT32INFOSECTOR_SIGNATURE_2;
4555 pInfoSector->uSignature3 = FAT32INFOSECTOR_SIGNATURE_3;
4556 pInfoSector->cFreeClusters = cClusters - 1; /* ASSUMES 1 cluster for the root dir. */
4557 pInfoSector->cLastAllocatedCluster = FAT_FIRST_DATA_CLUSTER;
4558 rc = RTVfsFileWriteAt(hVfsFile, offVol + cbSector, pInfoSector, cbSector, NULL);
4559
4560 uint32_t iSector = 2;
4561 if (RT_SUCCESS(rc))
4562 {
4563 pszLastOp = "fat32 unused reserved sectors";
4564 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
4565 (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo - iSector) * cbSector);
4566 iSector = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo;
4567 }
4568
4569 if (RT_SUCCESS(rc))
4570 {
4571 pszLastOp = "boot sector copy";
4572 for (uint32_t i = 0; i < 3 && RT_SUCCESS(rc); i++, iSector++)
4573 rc = RTVfsFileWriteAt(hVfsFile, offVol + iSector * cbSector, pBootSector, cbSector, NULL);
4574 }
4575
4576 if (RT_SUCCESS(rc))
4577 {
4578 pszLastOp = "fat32 unused reserved sectors";
4579 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
4580 (pBootSector->Bpb.Bpb331.cReservedSectors - iSector) * cbSector);
4581 }
4582 }
4583
4584 /*
4585 * The FATs.
4586 */
4587 if (RT_SUCCESS(rc))
4588 {
4589 pszLastOp = "fat";
4590 pBootSector = NULL; /* invalid */
4591 RT_BZERO(pbBuf, cbSector);
4592 switch (enmFatType)
4593 {
4594 case RTFSFATTYPE_FAT32:
4595 pbBuf[11] = 0x0f; /* EOC for root dir*/
4596 pbBuf[10] = 0xff;
4597 pbBuf[9] = 0xff;
4598 pbBuf[8] = 0xff;
4599 pbBuf[7] = 0x0f; /* Formatter's EOC, followed by signed extend FAT ID. */
4600 pbBuf[6] = 0xff;
4601 pbBuf[5] = 0xff;
4602 pbBuf[4] = 0xff;
4603 /* fall thru */
4604 case RTFSFATTYPE_FAT16:
4605 pbBuf[3] = 0xff;
4606 /* fall thru */
4607 case RTFSFATTYPE_FAT12:
4608 pbBuf[2] = 0xff;
4609 pbBuf[1] = 0xff;
4610 pbBuf[0] = bMedia; /* FAT ID */
4611 break;
4612 default: AssertFailed();
4613 }
4614 for (uint32_t iFatCopy = 0; iFatCopy < cFats && RT_SUCCESS(rc); iFatCopy++)
4615 {
4616 rc = RTVfsFileWriteAt(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy, pbBuf, cbSector, NULL);
4617 if (RT_SUCCESS(rc) && cbFat > cbSector)
4618 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy + cbSector, cbFat - cbSector);
4619 }
4620 }
4621
4622 /*
4623 * The root directory.
4624 */
4625 if (RT_SUCCESS(rc))
4626 {
4627 /** @todo any mandatory directory entries we need to fill in here? */
4628 pszLastOp = "root dir";
4629 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * cFats, cbRootDir);
4630 }
4631
4632 /*
4633 * If long format, fill the rest of the disk with 0xf6.
4634 */
4635 AssertCompile(RTFSFATVOL_FMT_F_QUICK != 0);
4636 if (RT_SUCCESS(rc) && !(fFlags & RTFSFATVOL_FMT_F_QUICK))
4637 {
4638 pszLastOp = "formatting data clusters";
4639 uint64_t offCur = offFirstFat + cbFat * cFats + cbRootDir;
4640 uint64_t cbLeft = cTotalSectors * cbSector;
4641 if (cbLeft > offCur)
4642 {
4643 cbLeft -= offCur;
4644 offCur += offVol;
4645
4646 memset(pbBuf, 0xf6, cbBuf);
4647 while (cbLeft > 0)
4648 {
4649 size_t cbToWrite = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
4650 rc = RTVfsFileWriteAt(hVfsFile, offCur, pbBuf, cbToWrite, NULL);
4651 if (RT_SUCCESS(rc))
4652 {
4653 offCur += cbToWrite;
4654 cbLeft -= cbToWrite;
4655 }
4656 else
4657 break;
4658 }
4659 }
4660 }
4661
4662 /*
4663 * Done.
4664 */
4665 RTMemTmpFree(pbBuf);
4666 if (RT_SUCCESS(rc))
4667 return rc;
4668 return RTErrInfoSet(pErrInfo, rc, pszLastOp);
4669}
4670
4671
4672/**
4673 * Formats a 1.44MB floppy image.
4674 *
4675 * @returns IPRT status code.
4676 * @param hVfsFile The image.
4677 */
4678RTDECL(int) RTFsFatVolFormat144(RTVFSFILE hVfsFile, bool fQuick)
4679{
4680 return RTFsFatVolFormat(hVfsFile, 0 /*offVol*/, 1474560, fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
4681 512 /*cbSector*/, 2 /*cSectorsPerCluster*/, RTFSFATTYPE_FAT12, 2 /*cHeads*/, 18 /*cSectors*/,
4682 0xf0 /*bMedia*/, 0 /*cHiddenSectors*/, 224 /*cRootDirEntries*/, NULL /*pErrInfo*/);
4683}
4684
4685
4686/**
4687 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
4688 */
4689static DECLCALLBACK(int) rtVfsChainFatVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
4690 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
4691{
4692 RT_NOREF(pProviderReg);
4693
4694 /*
4695 * Basic checks.
4696 */
4697 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
4698 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
4699 if ( pElement->enmType != RTVFSOBJTYPE_VFS
4700 && pElement->enmType != RTVFSOBJTYPE_DIR)
4701 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
4702 if (pElement->cArgs > 1)
4703 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
4704
4705 /*
4706 * Parse the flag if present, save in pElement->uProvider.
4707 */
4708 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
4709 if (pElement->cArgs > 0)
4710 {
4711 const char *psz = pElement->paArgs[0].psz;
4712 if (*psz)
4713 {
4714 if (!strcmp(psz, "ro"))
4715 fReadOnly = true;
4716 else if (!strcmp(psz, "rw"))
4717 fReadOnly = false;
4718 else
4719 {
4720 *poffError = pElement->paArgs[0].offSpec;
4721 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
4722 }
4723 }
4724 }
4725
4726 pElement->uProvider = fReadOnly;
4727 return VINF_SUCCESS;
4728}
4729
4730
4731/**
4732 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
4733 */
4734static DECLCALLBACK(int) rtVfsChainFatVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
4735 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
4736 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
4737{
4738 RT_NOREF(pProviderReg, pSpec, poffError);
4739
4740 int rc;
4741 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
4742 if (hVfsFileIn != NIL_RTVFSFILE)
4743 {
4744 RTVFS hVfs;
4745 rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, 0, &hVfs, pErrInfo);
4746 RTVfsFileRelease(hVfsFileIn);
4747 if (RT_SUCCESS(rc))
4748 {
4749 *phVfsObj = RTVfsObjFromVfs(hVfs);
4750 RTVfsRelease(hVfs);
4751 if (*phVfsObj != NIL_RTVFSOBJ)
4752 return VINF_SUCCESS;
4753 rc = VERR_VFS_CHAIN_CAST_FAILED;
4754 }
4755 }
4756 else
4757 rc = VERR_VFS_CHAIN_CAST_FAILED;
4758 return rc;
4759}
4760
4761
4762/**
4763 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
4764 */
4765static DECLCALLBACK(bool) rtVfsChainFatVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
4766 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
4767 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
4768{
4769 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
4770 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
4771 || !pReuseElement->paArgs[0].uProvider)
4772 return true;
4773 return false;
4774}
4775
4776
4777/** VFS chain element 'file'. */
4778static RTVFSCHAINELEMENTREG g_rtVfsChainFatVolReg =
4779{
4780 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
4781 /* fReserved = */ 0,
4782 /* pszName = */ "fat",
4783 /* ListEntry = */ { NULL, NULL },
4784 /* pszHelp = */ "Open a FAT file system, requires a file object on the left side.\n"
4785 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
4786 /* pfnValidate = */ rtVfsChainFatVol_Validate,
4787 /* pfnInstantiate = */ rtVfsChainFatVol_Instantiate,
4788 /* pfnCanReuseElement = */ rtVfsChainFatVol_CanReuseElement,
4789 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
4790};
4791
4792RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainFatVolReg, rtVfsChainFatVolReg);
4793
Note: See TracBrowser for help on using the repository browser.

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