VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/fatvfs.cpp@ 66734

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

fatvfs.cpp: nits

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