VirtualBox

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

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

iso9660vfs,fatvfs: reference count fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 191.1 KB
Line 
1/* $Id: fatvfs.cpp 66736 2017-05-02 00:01:39Z 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.pVol->pRootDir == pDir)
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 bool const fIsRootDir = pDir->Core.pVol->pRootDir == pDir;
3605
3606 uint32_t cRefs = RTVfsDirRelease(pDir->hVfsSelf);
3607 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3608
3609 /* Root directory releases the file system as well. Since the volume
3610 holds a reference to the root directory, it will remain valid after
3611 the above release. */
3612 if (fIsRootDir)
3613 {
3614 Assert(cRefs > 0);
3615 Assert(pDir->Core.pVol);
3616 Assert(pDir->Core.pVol == pChild->pVol);
3617 cRefs = RTVfsRelease(pDir->Core.pVol->hVfsSelf);
3618 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3619 }
3620 }
3621}
3622
3623
3624/**
3625 * Instantiates a new directory.
3626 *
3627 * @returns IPRT status code.
3628 * @param pThis The FAT volume instance.
3629 * @param pParentDir The parent directory. This is NULL for the root
3630 * directory.
3631 * @param pDirEntry The parent directory entry. This is NULL for the
3632 * root directory.
3633 * @param offEntryInDir The byte offset of the directory entry in the parent
3634 * directory. UINT32_MAX if root directory.
3635 * @param idxCluster The cluster where the directory content is to be
3636 * found. This can be UINT32_MAX if a root FAT12/16
3637 * directory.
3638 * @param offDisk The disk byte offset of the FAT12/16 root directory.
3639 * This is UINT64_MAX if idxCluster is given.
3640 * @param cbDir The size of the directory.
3641 * @param phVfsDir Where to return the directory handle.
3642 * @param ppDir Where to return the FAT directory instance data.
3643 */
3644static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
3645 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir, PRTFSFATDIR *ppDir)
3646{
3647 Assert((idxCluster == UINT32_MAX) != (offDisk == UINT64_MAX));
3648 Assert((pDirEntry == NULL) == (offEntryInDir == UINT32_MAX));
3649 if (ppDir)
3650 *ppDir = NULL;
3651
3652 PRTFSFATDIR pNewDir;
3653 int rc = RTVfsNewDir(&g_rtFsFatDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
3654 phVfsDir, (void **)&pNewDir);
3655 if (RT_SUCCESS(rc))
3656 {
3657 /*
3658 * Initialize it all so rtFsFatDir_Close doesn't trip up in anyway.
3659 */
3660 RTListInit(&pNewDir->OpenChildren);
3661 if (pDirEntry)
3662 rtFsFatObj_InitFromDirEntry(&pNewDir->Core, pDirEntry, offEntryInDir, pThis);
3663 else
3664 rtFsFatObj_InitDummy(&pNewDir->Core, cbDir, RTFS_DOS_DIRECTORY, pThis);
3665
3666 pNewDir->hVfsSelf = *phVfsDir;
3667 pNewDir->cEntries = cbDir / sizeof(FATDIRENTRY);
3668 pNewDir->fIsLinearRootDir = idxCluster == UINT32_MAX;
3669 pNewDir->fFullyBuffered = pNewDir->fIsLinearRootDir;
3670 pNewDir->paEntries = NULL;
3671 pNewDir->offEntriesOnDisk = UINT64_MAX;
3672 if (pNewDir->fFullyBuffered)
3673 pNewDir->cbAllocatedForEntries = RT_ALIGN_32(cbDir, pThis->cbSector);
3674 else
3675 pNewDir->cbAllocatedForEntries = pThis->cbSector;
3676
3677 /*
3678 * If clustered backing, read the chain and see if we cannot still do the full buffering.
3679 */
3680 if (idxCluster != UINT32_MAX)
3681 {
3682 rc = rtFsFatClusterMap_ReadClusterChain(pThis, idxCluster, &pNewDir->Core.Clusters);
3683 if (RT_SUCCESS(rc))
3684 {
3685 if ( pNewDir->Core.Clusters.cClusters >= 1
3686 && pNewDir->Core.Clusters.cbChain <= _64K
3687 && rtFsFatChain_IsContiguous(&pNewDir->Core.Clusters))
3688 {
3689 Assert(pNewDir->Core.Clusters.cbChain >= cbDir);
3690 pNewDir->cbAllocatedForEntries = pNewDir->Core.Clusters.cbChain;
3691 pNewDir->fFullyBuffered = true;
3692 }
3693 }
3694 }
3695 else
3696 rtFsFatChain_InitEmpty(&pNewDir->Core.Clusters, pThis);
3697 if (RT_SUCCESS(rc))
3698 {
3699 /*
3700 * Allocate and initialize the buffering. Fill the buffer.
3701 */
3702 pNewDir->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pNewDir->cbAllocatedForEntries);
3703 if (!pNewDir->paEntries)
3704 {
3705 if (pNewDir->fFullyBuffered && !pNewDir->fIsLinearRootDir)
3706 {
3707 pNewDir->fFullyBuffered = false;
3708 pNewDir->cbAllocatedForEntries = pThis->cbSector;
3709 pNewDir->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pNewDir->cbAllocatedForEntries);
3710 }
3711 if (!pNewDir->paEntries)
3712 rc = VERR_NO_MEMORY;
3713 }
3714
3715 if (RT_SUCCESS(rc))
3716 {
3717 if (pNewDir->fFullyBuffered)
3718 {
3719 pNewDir->u.Full.cDirtySectors = 0;
3720 pNewDir->u.Full.cSectors = pNewDir->cbAllocatedForEntries / pThis->cbSector;
3721 pNewDir->u.Full.pbDirtySectors = (uint8_t *)RTMemAllocZ((pNewDir->u.Full.cSectors + 63) / 8);
3722 if (pNewDir->u.Full.pbDirtySectors)
3723 pNewDir->offEntriesOnDisk = offDisk != UINT64_MAX ? offDisk
3724 : rtFsFatClusterToDiskOffset(pThis, idxCluster);
3725 else
3726 rc = VERR_NO_MEMORY;
3727 }
3728 else
3729 {
3730 pNewDir->offEntriesOnDisk = rtFsFatClusterToDiskOffset(pThis, idxCluster);
3731 pNewDir->u.Simple.offInDir = 0;
3732 pNewDir->u.Simple.fDirty = false;
3733 }
3734 if (RT_SUCCESS(rc))
3735 rc = RTVfsFileReadAt(pThis->hVfsBacking, pNewDir->offEntriesOnDisk,
3736 pNewDir->paEntries, pNewDir->cbAllocatedForEntries, NULL);
3737 if (RT_SUCCESS(rc))
3738 {
3739 /*
3740 * Link into parent directory so we can use it to update
3741 * our directory entry.
3742 */
3743 if (pParentDir)
3744 rtFsFatDir_AddOpenChild(pParentDir, &pNewDir->Core);
3745 if (ppDir)
3746 *ppDir = pNewDir;
3747 return VINF_SUCCESS;
3748 }
3749 }
3750
3751 /* Free the buffer on failure so rtFsFatDir_Close doesn't try do anything with it. */
3752 RTMemFree(pNewDir->paEntries);
3753 pNewDir->paEntries = NULL;
3754 }
3755
3756 RTVfsDirRelease(*phVfsDir);
3757 }
3758 *phVfsDir = NIL_RTVFSDIR;
3759 if (ppDir)
3760 *ppDir = NULL;
3761 return rc;
3762}
3763
3764
3765
3766
3767
3768/**
3769 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
3770 */
3771static DECLCALLBACK(int) rtFsFatVol_Close(void *pvThis)
3772{
3773 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
3774 int rc = rtFsFatClusterMap_Destroy(pThis);
3775
3776 if (pThis->hVfsRootDir != NIL_RTVFSDIR)
3777 {
3778 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
3779 uint32_t cRefs = RTVfsDirRelease(pThis->hVfsRootDir);
3780 Assert(cRefs == 0); NOREF(cRefs);
3781 pThis->hVfsRootDir = NIL_RTVFSDIR;
3782 pThis->pRootDir = NULL;
3783 }
3784
3785 RTVfsFileRelease(pThis->hVfsBacking);
3786 pThis->hVfsBacking = NIL_RTVFSFILE;
3787
3788 return rc;
3789}
3790
3791
3792/**
3793 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
3794 */
3795static DECLCALLBACK(int) rtFsFatVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3796{
3797 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
3798 return VERR_WRONG_TYPE;
3799}
3800
3801
3802/**
3803 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
3804 */
3805static DECLCALLBACK(int) rtFsFatVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
3806{
3807 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
3808 uint32_t cRefs = RTVfsDirRetain(pThis->hVfsRootDir);
3809 if (cRefs != UINT32_MAX)
3810 {
3811 *phVfsDir = pThis->hVfsRootDir;
3812 return VINF_SUCCESS;
3813 }
3814 return VERR_INTERNAL_ERROR_5;
3815}
3816
3817
3818/**
3819 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
3820 */
3821static DECLCALLBACK(int) rtFsFatVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
3822{
3823 RT_NOREF(pvThis, off, cb, pfUsed);
3824 return VERR_NOT_IMPLEMENTED;
3825}
3826
3827
3828DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsFatVolOps =
3829{
3830 { /* Obj */
3831 RTVFSOBJOPS_VERSION,
3832 RTVFSOBJTYPE_VFS,
3833 "FatVol",
3834 rtFsFatVol_Close,
3835 rtFsFatVol_QueryInfo,
3836 RTVFSOBJOPS_VERSION
3837 },
3838 RTVFSOPS_VERSION,
3839 0 /* fFeatures */,
3840 rtFsFatVol_OpenRoot,
3841 rtFsFatVol_IsRangeInUse,
3842 RTVFSOPS_VERSION
3843};
3844
3845
3846/**
3847 * Tries to detect a DOS 1.x formatted image and fills in the BPB fields.
3848 *
3849 * There is no BPB here, but fortunately, there isn't much variety.
3850 *
3851 * @returns IPRT status code.
3852 * @param pThis The FAT volume instance, BPB derived fields are filled
3853 * in on success.
3854 * @param pBootSector The boot sector.
3855 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
3856 * the boot sector.
3857 * @param pErrInfo Where to return additional error information.
3858 */
3859static int rtFsFatVolTryInitDos1x(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t const *pbFatSector,
3860 PRTERRINFO pErrInfo)
3861{
3862 /*
3863 * PC-DOS 1.0 does a 2fh byte short jump w/o any NOP following it.
3864 * Instead the following are three words and a 9 byte build date
3865 * string. The remaining space is zero filled.
3866 *
3867 * Note! No idea how this would look like for 8" floppies, only got 5"1/4'.
3868 *
3869 * ASSUME all non-BPB disks are using this format.
3870 */
3871 if ( pBootSector->abJmp[0] != 0xeb /* jmp rel8 */
3872 || pBootSector->abJmp[1] < 0x2f
3873 || pBootSector->abJmp[1] >= 0x80
3874 || pBootSector->abJmp[2] == 0x90 /* nop */)
3875 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3876 "No DOS v1.0 bootsector either - invalid jmp: %.3Rhxs", pBootSector->abJmp);
3877 uint32_t const offJump = 2 + pBootSector->abJmp[1];
3878 uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
3879 Assert(offFirstZero >= RT_UOFFSETOF(FATBOOTSECTOR, Bpb));
3880 uint32_t const cbZeroPad = RT_MIN(offJump - offFirstZero,
3881 sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_OFFSETOF(FATBOOTSECTOR, Bpb)));
3882
3883 if (!ASMMemIsAllU8((uint8_t const *)pBootSector + offFirstZero, cbZeroPad, 0))
3884 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3885 "No DOS v1.0 bootsector either - expected zero padding %#x LB %#x: %.*Rhxs",
3886 offFirstZero, cbZeroPad, cbZeroPad, (uint8_t const *)pBootSector + offFirstZero);
3887
3888 /*
3889 * Check the FAT ID so we can tell if this is double or single sided,
3890 * as well as being a valid FAT12 start.
3891 */
3892 if ( (pbFatSector[0] != 0xfe && pbFatSector[0] != 0xff)
3893 || pbFatSector[1] != 0xff
3894 || pbFatSector[2] != 0xff)
3895 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3896 "No DOS v1.0 bootsector either - unexpected start of FAT: %.3Rhxs", pbFatSector);
3897
3898 /*
3899 * Fixed DOS 1.0 config.
3900 */
3901 pThis->enmFatType = RTFSFATTYPE_FAT12;
3902 pThis->enmBpbVersion = RTFSFATBPBVER_NO_BPB;
3903 pThis->bMedia = pbFatSector[0];
3904 pThis->cReservedSectors = 1;
3905 pThis->cbSector = 512;
3906 pThis->cbCluster = pThis->bMedia == 0xfe ? 1024 : 512;
3907 pThis->cFats = 2;
3908 pThis->cbFat = 512;
3909 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * 512;
3910 pThis->aoffFats[1] = pThis->aoffFats[0] + pThis->cbFat;
3911 pThis->offRootDir = pThis->aoffFats[1] + pThis->cbFat;
3912 pThis->cRootDirEntries = 512;
3913 pThis->offFirstCluster = pThis->offRootDir + RT_ALIGN_32(pThis->cRootDirEntries * sizeof(FATDIRENTRY),
3914 pThis->cbSector);
3915 pThis->cbTotalSize = pThis->bMedia == 0xfe ? 8 * 1 * 40 * 512 : 8 * 2 * 40 * 512;
3916 pThis->cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
3917 return VINF_SUCCESS;
3918}
3919
3920
3921/**
3922 * Worker for rtFsFatVolTryInitDos2Plus that handles remaining BPB fields.
3923 *
3924 * @returns IPRT status code.
3925 * @param pThis The FAT volume instance, BPB derived fields are filled
3926 * in on success.
3927 * @param pBootSector The boot sector.
3928 * @param fMaybe331 Set if it could be a DOS v3.31 BPB.
3929 * @param pErrInfo Where to return additional error information.
3930 */
3931static int rtFsFatVolTryInitDos2PlusBpb(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, bool fMaybe331, PRTERRINFO pErrInfo)
3932{
3933 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_2_0;
3934
3935 /*
3936 * Figure total sector count. Could both be zero, in which case we have to
3937 * fall back on the size of the backing stuff.
3938 */
3939 if (pBootSector->Bpb.Bpb20.cTotalSectors16 != 0)
3940 pThis->cbTotalSize = pBootSector->Bpb.Bpb20.cTotalSectors16 * pThis->cbSector;
3941 else if ( pBootSector->Bpb.Bpb331.cTotalSectors32 != 0
3942 && fMaybe331)
3943 {
3944 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_3_31;
3945 pThis->cbTotalSize = pBootSector->Bpb.Bpb331.cTotalSectors32 * (uint64_t)pThis->cbSector;
3946 }
3947 else
3948 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
3949 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
3950 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3951 "Bogus FAT12/16 total or reserved sector count: %#x vs %#x",
3952 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
3953
3954 /*
3955 * The fat size. Complete FAT offsets.
3956 */
3957 if ( pBootSector->Bpb.Bpb20.cSectorsPerFat == 0
3958 || ((uint32_t)pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cFats + 1) * pThis->cbSector > pThis->cbTotalSize)
3959 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 sectors per FAT: %#x (total sectors %#RX64)",
3960 pBootSector->Bpb.Bpb20.cSectorsPerFat, pThis->cbTotalSize / pThis->cbSector);
3961 pThis->cbFat = pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cbSector;
3962
3963 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
3964 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
3965 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
3966
3967 /*
3968 * Do root directory calculations.
3969 */
3970 pThis->idxRootDirCluster = UINT32_MAX;
3971 pThis->offRootDir = pThis->aoffFats[pThis->cFats];
3972 if (pThis->cRootDirEntries == 0)
3973 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero FAT12/16 root directory size");
3974 pThis->cbRootDir = pThis->cRootDirEntries * sizeof(FATDIRENTRY);
3975 pThis->cbRootDir = RT_ALIGN_32(pThis->cbRootDir, pThis->cbSector);
3976
3977 /*
3978 * First cluster and cluster count checks and calcs. Determin FAT type.
3979 */
3980 pThis->offFirstCluster = pThis->offRootDir + pThis->cbRootDir;
3981 uint64_t cbSystemStuff = pThis->offFirstCluster - pThis->offBootSector;
3982 if (cbSystemStuff >= pThis->cbTotalSize)
3983 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 total size, root dir, or fat size");
3984 pThis->cClusters = (pThis->cbTotalSize - cbSystemStuff) / pThis->cbCluster;
3985
3986 if (pThis->cClusters >= FAT_MAX_FAT16_DATA_CLUSTERS)
3987 {
3988 pThis->cClusters = FAT_MAX_FAT16_DATA_CLUSTERS;
3989 pThis->enmFatType = RTFSFATTYPE_FAT16;
3990 }
3991 else if (pThis->cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS)
3992 pThis->enmFatType = RTFSFATTYPE_FAT16;
3993 else
3994 pThis->enmFatType = RTFSFATTYPE_FAT12; /** @todo Not sure if this is entirely the right way to go about it... */
3995
3996 uint32_t cClustersPerFat;
3997 if (pThis->enmFatType == RTFSFATTYPE_FAT16)
3998 cClustersPerFat = pThis->cbFat / 2;
3999 else
4000 cClustersPerFat = pThis->cbFat * 2 / 3;
4001 if (pThis->cClusters > cClustersPerFat)
4002 pThis->cClusters = cClustersPerFat;
4003
4004 return VINF_SUCCESS;
4005}
4006
4007
4008/**
4009 * Worker for rtFsFatVolTryInitDos2Plus and rtFsFatVolTryInitDos2PlusFat32 that
4010 * handles common extended BPBs fields.
4011 *
4012 * @returns IPRT status code.
4013 * @param pThis The FAT volume instance.
4014 * @param bExtSignature The extended BPB signature.
4015 * @param uSerialNumber The serial number.
4016 * @param pachLabel Pointer to the volume label field.
4017 * @param pachType Pointer to the file system type field.
4018 */
4019static void rtFsFatVolInitCommonEbpbBits(PRTFSFATVOL pThis, uint8_t bExtSignature, uint32_t uSerialNumber,
4020 char const *pachLabel, char const *pachType)
4021{
4022 pThis->uSerialNo = uSerialNumber;
4023 if (bExtSignature == FATEBPB_SIGNATURE)
4024 {
4025 memcpy(pThis->szLabel, pachLabel, RT_SIZEOFMEMB(FATEBPB, achLabel));
4026 pThis->szLabel[RT_SIZEOFMEMB(FATEBPB, achLabel)] = '\0';
4027 RTStrStrip(pThis->szLabel);
4028
4029 memcpy(pThis->szType, pachType, RT_SIZEOFMEMB(FATEBPB, achType));
4030 pThis->szType[RT_SIZEOFMEMB(FATEBPB, achType)] = '\0';
4031 RTStrStrip(pThis->szType);
4032 }
4033 else
4034 {
4035 pThis->szLabel[0] = '\0';
4036 pThis->szType[0] = '\0';
4037 }
4038}
4039
4040
4041/**
4042 * Worker for rtFsFatVolTryInitDos2Plus that deals with FAT32.
4043 *
4044 * @returns IPRT status code.
4045 * @param pThis The FAT volume instance, BPB derived fields are filled
4046 * in on success.
4047 * @param pBootSector The boot sector.
4048 * @param pErrInfo Where to return additional error information.
4049 */
4050static int rtFsFatVolTryInitDos2PlusFat32(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, PRTERRINFO pErrInfo)
4051{
4052 pThis->enmFatType = RTFSFATTYPE_FAT32;
4053 pThis->enmBpbVersion = pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE
4054 ? RTFSFATBPBVER_FAT32_29 : RTFSFATBPBVER_FAT32_28;
4055 pThis->fFat32Flags = pBootSector->Bpb.Fat32Ebpb.fFlags;
4056
4057 if (pBootSector->Bpb.Fat32Ebpb.uVersion != FAT32EBPB_VERSION_0_0)
4058 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Unsupported FAT32 version: %d.%d (%#x)",
4059 RT_HI_U8(pBootSector->Bpb.Fat32Ebpb.uVersion), RT_LO_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),
4060 pBootSector->Bpb.Fat32Ebpb.uVersion);
4061
4062 /*
4063 * Figure total sector count. We expected it to be filled in.
4064 */
4065 bool fUsing64BitTotalSectorCount = false;
4066 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 != 0)
4067 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 * pThis->cbSector;
4068 else if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 != 0)
4069 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 * (uint64_t)pThis->cbSector;
4070 else if ( pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 <= UINT64_MAX / 512
4071 && pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 > 3
4072 && pBootSector->Bpb.Fat32Ebpb.bExtSignature != FATEBPB_SIGNATURE_OLD)
4073 {
4074 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 * pThis->cbSector;
4075 fUsing64BitTotalSectorCount = true;
4076 }
4077 else
4078 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 total sector count out of range: %#RX64",
4079 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64);
4080 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
4081 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4082 "Bogus FAT32 total or reserved sector count: %#x vs %#x",
4083 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
4084
4085 /*
4086 * Fat size. We check the 16-bit field even if it probably should be zero all the time.
4087 */
4088 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat != 0)
4089 {
4090 if ( pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != 0
4091 && pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat)
4092 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4093 "Both 16-bit and 32-bit FAT size fields are set: %#RX16 vs %#RX32",
4094 pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat, pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
4095 pThis->cbFat = pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat * pThis->cbSector;
4096 }
4097 else
4098 {
4099 uint64_t cbFat = pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 * (uint64_t)pThis->cbSector;
4100 if ( cbFat == 0
4101 || cbFat >= FAT_MAX_FAT32_TOTAL_CLUSTERS * 4 + pThis->cbSector * 16)
4102 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4103 "Bogus 32-bit FAT size: %#RX32", pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
4104 pThis->cbFat = (uint32_t)cbFat;
4105 }
4106
4107 /*
4108 * Complete the FAT offsets and first cluster offset, then calculate number
4109 * of data clusters.
4110 */
4111 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
4112 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
4113 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
4114 pThis->offFirstCluster = pThis->aoffFats[pThis->cFats];
4115
4116 if (pThis->offFirstCluster - pThis->offBootSector >= pThis->cbTotalSize)
4117 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4118 "Bogus 32-bit FAT size or total sector count: cFats=%d cbFat=%#x cbTotalSize=%#x",
4119 pThis->cFats, pThis->cbFat, pThis->cbTotalSize);
4120
4121 uint64_t cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
4122 if (cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS)
4123 pThis->cClusters = (uint32_t)cClusters;
4124 else
4125 pThis->cClusters = FAT_MAX_FAT32_DATA_CLUSTERS;
4126 if (pThis->cClusters > (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER))
4127 pThis->cClusters = (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER);
4128
4129 /*
4130 * Root dir cluster.
4131 */
4132 if ( pBootSector->Bpb.Fat32Ebpb.uRootDirCluster < FAT_FIRST_DATA_CLUSTER
4133 || pBootSector->Bpb.Fat32Ebpb.uRootDirCluster >= pThis->cClusters)
4134 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4135 "Bogus FAT32 root directory cluster: %#x", pBootSector->Bpb.Fat32Ebpb.uRootDirCluster);
4136 pThis->idxRootDirCluster = pBootSector->Bpb.Fat32Ebpb.uRootDirCluster;
4137 pThis->offRootDir = pThis->offFirstCluster
4138 + (pBootSector->Bpb.Fat32Ebpb.uRootDirCluster - FAT_FIRST_DATA_CLUSTER) * pThis->cbCluster;
4139
4140 /*
4141 * Info sector.
4142 */
4143 if ( pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == 0
4144 || pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == UINT16_MAX)
4145 pThis->offFat32InfoSector = UINT64_MAX;
4146 else if (pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo >= pThis->cReservedSectors)
4147 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4148 "Bogus FAT32 info sector number: %#x (reserved sectors %#x)",
4149 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pThis->cReservedSectors);
4150 else
4151 {
4152 pThis->offFat32InfoSector = pThis->cbSector * pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo + pThis->offBootSector;
4153 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->offFat32InfoSector,
4154 &pThis->Fat32InfoSector, sizeof(pThis->Fat32InfoSector), NULL);
4155 if (RT_FAILURE(rc))
4156 return RTErrInfoSetF(pErrInfo, rc, "Failed to read FAT32 info sector at offset %#RX64", pThis->offFat32InfoSector);
4157 if ( pThis->Fat32InfoSector.uSignature1 != FAT32INFOSECTOR_SIGNATURE_1
4158 || pThis->Fat32InfoSector.uSignature2 != FAT32INFOSECTOR_SIGNATURE_2
4159 || pThis->Fat32InfoSector.uSignature3 != FAT32INFOSECTOR_SIGNATURE_3)
4160 return RTErrInfoSetF(pErrInfo, rc, "FAT32 info sector signature mismatch: %#x %#x %#x",
4161 pThis->Fat32InfoSector.uSignature1, pThis->Fat32InfoSector.uSignature2,
4162 pThis->Fat32InfoSector.uSignature3);
4163 }
4164
4165 /*
4166 * Boot sector copy.
4167 */
4168 if ( pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == 0
4169 || pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == UINT16_MAX)
4170 {
4171 pThis->cBootSectorCopies = 0;
4172 pThis->offBootSectorCopies = UINT64_MAX;
4173 }
4174 else if (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo >= pThis->cReservedSectors)
4175 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4176 "Bogus FAT32 info boot sector copy location: %#x (reserved sectors %#x)",
4177 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo, pThis->cReservedSectors);
4178 else
4179 {
4180 /** @todo not sure if cbSector is correct here. */
4181 pThis->cBootSectorCopies = 3;
4182 if ( (uint32_t)pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo + pThis->cBootSectorCopies
4183 > pThis->cReservedSectors)
4184 pThis->cBootSectorCopies = (uint8_t)(pThis->cReservedSectors - pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
4185 pThis->offBootSectorCopies = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo * pThis->cbSector + pThis->offBootSector;
4186 if ( pThis->offFat32InfoSector != UINT64_MAX
4187 && pThis->offFat32InfoSector - pThis->offBootSectorCopies < (uint64_t)(pThis->cBootSectorCopies * pThis->cbSector))
4188 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 info sector and boot sector copies overlap: %#x vs %#x",
4189 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
4190 }
4191
4192 /*
4193 * Serial number, label and type.
4194 */
4195 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Fat32Ebpb.bExtSignature, pBootSector->Bpb.Fat32Ebpb.uSerialNumber,
4196 pBootSector->Bpb.Fat32Ebpb.achLabel,
4197 fUsing64BitTotalSectorCount ? pBootSector->achOemName : pBootSector->Bpb.Fat32Ebpb.achLabel);
4198 if (pThis->szType[0] == '\0')
4199 memcpy(pThis->szType, "FAT32", 6);
4200
4201 return VINF_SUCCESS;
4202}
4203
4204
4205/**
4206 * Tries to detect a DOS 2.0+ formatted image and fills in the BPB fields.
4207 *
4208 * We ASSUME BPB here, but need to figure out which version of the BPB it is,
4209 * which is lots of fun.
4210 *
4211 * @returns IPRT status code.
4212 * @param pThis The FAT volume instance, BPB derived fields are filled
4213 * in on success.
4214 * @param pBootSector The boot sector.
4215 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
4216 * the boot sector. On successful return it will contain
4217 * the first FAT sector.
4218 * @param pErrInfo Where to return additional error information.
4219 */
4220static int rtFsFatVolTryInitDos2Plus(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t *pbFatSector, PRTERRINFO pErrInfo)
4221{
4222 /*
4223 * Check if we've got a known jump instruction first, because that will
4224 * give us a max (E)BPB size hint.
4225 */
4226 uint8_t offJmp = UINT8_MAX;
4227 if ( pBootSector->abJmp[0] == 0xeb
4228 && pBootSector->abJmp[1] <= 0x7f)
4229 offJmp = pBootSector->abJmp[1] + 2;
4230 else if ( pBootSector->abJmp[0] == 0x90
4231 && pBootSector->abJmp[1] == 0xeb
4232 && pBootSector->abJmp[2] <= 0x7f)
4233 offJmp = pBootSector->abJmp[2] + 3;
4234 else if ( pBootSector->abJmp[0] == 0xe9
4235 && pBootSector->abJmp[2] <= 0x7f)
4236 offJmp = RT_MIN(127, RT_MAKE_U16(pBootSector->abJmp[1], pBootSector->abJmp[2]));
4237 uint8_t const cbMaxBpb = offJmp - RT_OFFSETOF(FATBOOTSECTOR, Bpb);
4238
4239 /*
4240 * Do the basic DOS v2.0 BPB fields.
4241 */
4242 if (cbMaxBpb < sizeof(FATBPB20))
4243 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4244 "DOS signature, but jmp too short for any BPB: %#x (max %#x BPB)", offJmp, cbMaxBpb);
4245
4246 if (pBootSector->Bpb.Bpb20.cFats == 0)
4247 return RTErrInfoSet(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, number of FATs is zero, so not FAT file system");
4248 if (pBootSector->Bpb.Bpb20.cFats > 4)
4249 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many FATs: %#x", pBootSector->Bpb.Bpb20.cFats);
4250 pThis->cFats = pBootSector->Bpb.Bpb20.cFats;
4251
4252 if (!FATBPB_MEDIA_IS_VALID(pBootSector->Bpb.Bpb20.bMedia))
4253 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4254 "DOS signature, invalid media byte: %#x", pBootSector->Bpb.Bpb20.bMedia);
4255 pThis->bMedia = pBootSector->Bpb.Bpb20.bMedia;
4256
4257 if (!RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cbSector))
4258 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4259 "DOS signature, sector size not power of two: %#x", pBootSector->Bpb.Bpb20.cbSector);
4260 if ( pBootSector->Bpb.Bpb20.cbSector != 512
4261 && pBootSector->Bpb.Bpb20.cbSector != 4096
4262 && pBootSector->Bpb.Bpb20.cbSector != 1024
4263 && pBootSector->Bpb.Bpb20.cbSector != 128)
4264 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4265 "DOS signature, unsupported sector size: %#x", pBootSector->Bpb.Bpb20.cbSector);
4266 pThis->cbSector = pBootSector->Bpb.Bpb20.cbSector;
4267
4268 if ( !RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cSectorsPerCluster)
4269 || !pBootSector->Bpb.Bpb20.cSectorsPerCluster)
4270 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, cluster size not non-zero power of two: %#x",
4271 pBootSector->Bpb.Bpb20.cSectorsPerCluster);
4272 pThis->cbCluster = pBootSector->Bpb.Bpb20.cSectorsPerCluster * pThis->cbSector;
4273
4274 uint64_t const cMaxRoot = (pThis->cbBacking - pThis->offBootSector - 512) / sizeof(FATDIRENTRY); /* we'll check again later. */
4275 if (pBootSector->Bpb.Bpb20.cMaxRootDirEntries >= cMaxRoot)
4276 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many root entries: %#x (max %#RX64)",
4277 pBootSector->Bpb.Bpb20.cSectorsPerCluster, cMaxRoot);
4278 pThis->cRootDirEntries = pBootSector->Bpb.Bpb20.cMaxRootDirEntries;
4279
4280 if ( pBootSector->Bpb.Bpb20.cReservedSectors == 0
4281 || pBootSector->Bpb.Bpb20.cReservedSectors >= _32K)
4282 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4283 "DOS signature, bogus reserved sector count: %#x", pBootSector->Bpb.Bpb20.cReservedSectors);
4284 pThis->cReservedSectors = pBootSector->Bpb.Bpb20.cReservedSectors;
4285 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * pThis->cbSector;
4286
4287 /*
4288 * Jump ahead and check for FAT32 EBPB.
4289 * If found, we simply ASSUME it's a FAT32 file system.
4290 */
4291 int rc;
4292 if ( ( sizeof(FAT32EBPB) <= cbMaxBpb
4293 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE)
4294 || ( RT_OFFSETOF(FAT32EBPB, achLabel) <= cbMaxBpb
4295 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
4296 {
4297 rc = rtFsFatVolTryInitDos2PlusFat32(pThis, pBootSector, pErrInfo);
4298 if (RT_FAILURE(rc))
4299 return rc;
4300 }
4301 else
4302 {
4303 /*
4304 * Check for extended BPB, otherwise we'll have to make qualified guesses
4305 * about what kind of BPB we're up against based on jmp offset and zero fields.
4306 * ASSUMES either FAT16 or FAT12.
4307 */
4308 if ( ( sizeof(FATEBPB) <= cbMaxBpb
4309 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE)
4310 || ( RT_OFFSETOF(FATEBPB, achLabel) <= cbMaxBpb
4311 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
4312 {
4313 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Ebpb.bExtSignature, pBootSector->Bpb.Ebpb.uSerialNumber,
4314 pBootSector->Bpb.Ebpb.achLabel, pBootSector->Bpb.Ebpb.achType);
4315 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, true /*fMaybe331*/, pErrInfo);
4316 pThis->enmBpbVersion = pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE
4317 ? RTFSFATBPBVER_EXT_29 : RTFSFATBPBVER_EXT_28;
4318 }
4319 else
4320 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, cbMaxBpb >= sizeof(FATBPB331), pErrInfo);
4321 if (RT_FAILURE(rc))
4322 return rc;
4323 if (pThis->szType[0] == '\0')
4324 memcpy(pThis->szType, pThis->enmFatType == RTFSFATTYPE_FAT12 ? "FAT12" : "FAT16", 6);
4325 }
4326
4327 /*
4328 * Check the FAT ID. May have to read a bit of the FAT into the buffer.
4329 */
4330 if (pThis->aoffFats[0] != pThis->offBootSector + 512)
4331 {
4332 rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0], pbFatSector, 512, NULL);
4333 if (RT_FAILURE(rc))
4334 return RTErrInfoSet(pErrInfo, rc, "error reading first FAT sector");
4335 }
4336 if (pbFatSector[0] != pThis->bMedia)
4337 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4338 "Media byte and FAT ID mismatch: %#x vs %#x (%.7Rhxs)", pbFatSector[0], pThis->bMedia, pbFatSector);
4339 switch (pThis->enmFatType)
4340 {
4341 case RTFSFATTYPE_FAT12:
4342 if ((pbFatSector[1] & 0xf) != 0xf)
4343 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT12): %.3Rhxs", pbFatSector);
4344 pThis->idxMaxLastCluster = FAT_LAST_FAT12_DATA_CLUSTER;
4345 pThis->idxEndOfChain = (pbFatSector[1] >> 4) | ((uint32_t)pbFatSector[2] << 4);
4346 break;
4347
4348 case RTFSFATTYPE_FAT16:
4349 if (pbFatSector[1] != 0xff)
4350 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT16): %.4Rhxs", pbFatSector);
4351 pThis->idxMaxLastCluster = FAT_LAST_FAT16_DATA_CLUSTER;
4352 pThis->idxEndOfChain = RT_MAKE_U16(pbFatSector[2], pbFatSector[3]);
4353 break;
4354
4355 case RTFSFATTYPE_FAT32:
4356 if ( pbFatSector[1] != 0xff
4357 || pbFatSector[2] != 0xff
4358 || (pbFatSector[3] & 0x0f) != 0x0f)
4359 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT32): %.8Rhxs", pbFatSector);
4360 pThis->idxMaxLastCluster = FAT_LAST_FAT32_DATA_CLUSTER;
4361 pThis->idxEndOfChain = RT_MAKE_U32_FROM_U8(pbFatSector[4], pbFatSector[5], pbFatSector[6], pbFatSector[7]);
4362 break;
4363
4364 default: AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4365 }
4366 if (pThis->idxEndOfChain <= pThis->idxMaxLastCluster)
4367 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus formatter end-of-chain value: %#x, must be above %#x",
4368 pThis->idxEndOfChain, pThis->idxMaxLastCluster);
4369
4370 RT_NOREF(pbFatSector);
4371 return VINF_SUCCESS;
4372}
4373
4374
4375/**
4376 * Given a power of two value @a cb return exponent value.
4377 *
4378 * @returns Shift count
4379 * @param cb The value.
4380 */
4381static uint8_t rtFsFatVolCalcByteShiftCount(uint32_t cb)
4382{
4383 Assert(RT_IS_POWER_OF_TWO(cb));
4384 unsigned iBit = ASMBitFirstSetU32(cb);
4385 Assert(iBit >= 1);
4386 iBit--;
4387 return iBit;
4388}
4389
4390
4391/**
4392 * Worker for RTFsFatVolOpen.
4393 *
4394 * @returns IPRT status code.
4395 * @param pThis The FAT VFS instance to initialize.
4396 * @param hVfsSelf The FAT VFS handle (no reference consumed).
4397 * @param hVfsBacking The file backing the alleged FAT file system.
4398 * Reference is consumed (via rtFsFatVol_Destroy).
4399 * @param fReadOnly Readonly or readwrite mount.
4400 * @param offBootSector The boot sector offset in bytes.
4401 * @param pErrInfo Where to return additional error info. Can be NULL.
4402 */
4403static int rtFsFatVolTryInit(PRTFSFATVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking,
4404 bool fReadOnly, uint64_t offBootSector, PRTERRINFO pErrInfo)
4405{
4406 /*
4407 * First initialize the state so that rtFsFatVol_Destroy won't trip up.
4408 */
4409 pThis->hVfsSelf = hVfsSelf;
4410 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsFatVol_Destroy releases it. */
4411 pThis->cbBacking = 0;
4412 pThis->offBootSector = offBootSector;
4413 pThis->offNanoUTC = RTTimeLocalDeltaNano();
4414 pThis->offMinUTC = pThis->offNanoUTC / RT_NS_1MIN;
4415 pThis->fReadOnly = fReadOnly;
4416 pThis->cReservedSectors = 1;
4417
4418 pThis->cbSector = 512;
4419 pThis->cbCluster = 512;
4420 pThis->cClusters = 0;
4421 pThis->offFirstCluster = 0;
4422 pThis->cbTotalSize = 0;
4423
4424 pThis->enmFatType = RTFSFATTYPE_INVALID;
4425 pThis->cFatEntries = 0;
4426 pThis->cFats = 0;
4427 pThis->cbFat = 0;
4428 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aoffFats); i++)
4429 pThis->aoffFats[i] = UINT64_MAX;
4430 pThis->pFatCache = NULL;
4431
4432 pThis->offRootDir = UINT64_MAX;
4433 pThis->idxRootDirCluster = UINT32_MAX;
4434 pThis->cRootDirEntries = UINT32_MAX;
4435 pThis->cbRootDir = 0;
4436 pThis->hVfsRootDir = NIL_RTVFSDIR;
4437 pThis->pRootDir = NULL;
4438
4439 pThis->uSerialNo = 0;
4440 pThis->szLabel[0] = '\0';
4441 pThis->szType[0] = '\0';
4442 pThis->cBootSectorCopies = 0;
4443 pThis->fFat32Flags = 0;
4444 pThis->offBootSectorCopies = UINT64_MAX;
4445 pThis->offFat32InfoSector = UINT64_MAX;
4446 RT_ZERO(pThis->Fat32InfoSector);
4447
4448 /*
4449 * Get stuff that may fail.
4450 */
4451 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
4452 if (RT_FAILURE(rc))
4453 return rc;
4454 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
4455
4456 /*
4457 * Read the boot sector and the following sector (start of the allocation
4458 * table unless it a FAT32 FS). We'll then validate the boot sector and
4459 * start of the FAT, expanding the BPB into the instance data.
4460 */
4461 union
4462 {
4463 uint8_t ab[512*2];
4464 uint16_t au16[512*2 / 2];
4465 uint32_t au32[512*2 / 4];
4466 FATBOOTSECTOR BootSector;
4467 FAT32INFOSECTOR InfoSector;
4468 } Buf;
4469 RT_ZERO(Buf);
4470
4471 rc = RTVfsFileReadAt(hVfsBacking, offBootSector, &Buf.BootSector, 512 * 2, NULL);
4472 if (RT_FAILURE(rc))
4473 return RTErrInfoSet(pErrInfo, rc, "Unable to read bootsect");
4474
4475 /*
4476 * Extract info from the BPB and validate the two special FAT entries.
4477 *
4478 * Check the DOS signature first. The PC-DOS 1.0 boot floppy does not have
4479 * a signature and we ASSUME this is the case for all flopies formated by it.
4480 */
4481 if (Buf.BootSector.uSignature != FATBOOTSECTOR_SIGNATURE)
4482 {
4483 if (Buf.BootSector.uSignature != 0)
4484 return RTErrInfoSetF(pErrInfo, rc, "No DOS bootsector signature: %#06x", Buf.BootSector.uSignature);
4485 rc = rtFsFatVolTryInitDos1x(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
4486 }
4487 else
4488 rc = rtFsFatVolTryInitDos2Plus(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
4489 if (RT_FAILURE(rc))
4490 return rc;
4491
4492 /*
4493 * Calc shift counts.
4494 */
4495 pThis->cSectorByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbSector);
4496 pThis->cClusterByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbCluster);
4497
4498 /*
4499 * Setup the FAT cache.
4500 */
4501 rc = rtFsFatClusterMap_Create(pThis, &Buf.ab[512], pErrInfo);
4502 if (RT_FAILURE(rc))
4503 return rc;
4504
4505 /*
4506 * Create the root directory fun.
4507 */
4508 if (pThis->idxRootDirCluster == UINT32_MAX)
4509 rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
4510 UINT32_MAX, pThis->offRootDir, pThis->cbRootDir,
4511 &pThis->hVfsRootDir, &pThis->pRootDir);
4512 else
4513 rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
4514 pThis->idxRootDirCluster, UINT64_MAX, pThis->cbRootDir,
4515 &pThis->hVfsRootDir, &pThis->pRootDir);
4516 return rc;
4517}
4518
4519
4520/**
4521 * Opens a FAT file system volume.
4522 *
4523 * @returns IPRT status code.
4524 * @param hVfsFileIn The file or device backing the volume.
4525 * @param fReadOnly Whether to mount it read-only.
4526 * @param offBootSector The offset of the boot sector relative to the start
4527 * of @a hVfsFileIn. Pass 0 for floppies.
4528 * @param phVfs Where to return the virtual file system handle.
4529 * @param pErrInfo Where to return additional error information.
4530 */
4531RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, uint64_t offBootSector, PRTVFS phVfs, PRTERRINFO pErrInfo)
4532{
4533 /*
4534 * Quick input validation.
4535 */
4536 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
4537 *phVfs = NIL_RTVFS;
4538
4539 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
4540 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
4541
4542 /*
4543 * Create a new FAT VFS instance and try initialize it using the given input file.
4544 */
4545 RTVFS hVfs = NIL_RTVFS;
4546 void *pvThis = NULL;
4547 int rc = RTVfsNew(&g_rtFsFatVolOps, sizeof(RTFSFATVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
4548 if (RT_SUCCESS(rc))
4549 {
4550 rc = rtFsFatVolTryInit((PRTFSFATVOL)pvThis, hVfs, hVfsFileIn, fReadOnly, offBootSector, pErrInfo);
4551 if (RT_SUCCESS(rc))
4552 *phVfs = hVfs;
4553 else
4554 RTVfsRelease(hVfs);
4555 }
4556 else
4557 RTVfsFileRelease(hVfsFileIn);
4558 return rc;
4559}
4560
4561
4562
4563
4564/**
4565 * Fills a range in the file with zeros in the most efficient manner.
4566 *
4567 * @returns IPRT status code.
4568 * @param hVfsFile The file to write to.
4569 * @param off Where to start filling with zeros.
4570 * @param cbZeros How many zero blocks to write.
4571 */
4572static int rtFsFatVolWriteZeros(RTVFSFILE hVfsFile, uint64_t off, uint32_t cbZeros)
4573{
4574 while (cbZeros > 0)
4575 {
4576 uint32_t cbToWrite = sizeof(g_abRTZero64K);
4577 if (cbToWrite > cbZeros)
4578 cbToWrite = cbZeros;
4579 int rc = RTVfsFileWriteAt(hVfsFile, off, g_abRTZero64K, cbToWrite, NULL);
4580 if (RT_FAILURE(rc))
4581 return rc;
4582 off += cbToWrite;
4583 cbZeros -= cbToWrite;
4584 }
4585 return VINF_SUCCESS;
4586}
4587
4588
4589/**
4590 * Formats a FAT volume.
4591 *
4592 * @returns IRPT status code.
4593 * @param hVfsFile The volume file.
4594 * @param offVol The offset into @a hVfsFile of the file.
4595 * Typically 0.
4596 * @param cbVol The size of the volume. Pass 0 if the rest of
4597 * hVfsFile should be used.
4598 * @param fFlags See RTFSFATVOL_FMT_F_XXX.
4599 * @param cbSector The logical sector size. Must be power of two.
4600 * Optional, pass zero to use 512.
4601 * @param cSectorsPerCluster Number of sectors per cluster. Power of two.
4602 * Optional, pass zero to auto detect.
4603 * @param enmFatType The FAT type (12, 16, 32) to use.
4604 * Optional, pass RTFSFATTYPE_INVALID for default.
4605 * @param cHeads The number of heads to report in the BPB.
4606 * Optional, pass zero to auto detect.
4607 * @param cSectorsPerTrack The number of sectors per track to put in the
4608 * BPB. Optional, pass zero to auto detect.
4609 * @param bMedia The media byte value and FAT ID to use.
4610 * Optional, pass zero to auto detect.
4611 * @param cRootDirEntries Number of root directory entries.
4612 * Optional, pass zero to auto detect.
4613 * @param cHiddenSectors Number of hidden sectors. Pass 0 for
4614 * unpartitioned media.
4615 * @param pErrInfo Additional error information, maybe. Optional.
4616 */
4617RTDECL(int) RTFsFatVolFormat(RTVFSFILE hVfsFile, uint64_t offVol, uint64_t cbVol, uint32_t fFlags, uint16_t cbSector,
4618 uint16_t cSectorsPerCluster, RTFSFATTYPE enmFatType, uint32_t cHeads, uint32_t cSectorsPerTrack,
4619 uint8_t bMedia, uint16_t cRootDirEntries, uint32_t cHiddenSectors, PRTERRINFO pErrInfo)
4620{
4621 int rc;
4622 uint32_t cFats = 2;
4623
4624 /*
4625 * Validate input.
4626 */
4627 if (!cbSector)
4628 cbSector = 512;
4629 else
4630 AssertMsgReturn( cbSector == 128
4631 || cbSector == 512
4632 || cbSector == 1024
4633 || cbSector == 4096,
4634 ("cbSector=%#x\n", cbSector),
4635 VERR_INVALID_PARAMETER);
4636 AssertMsgReturn(cSectorsPerCluster == 0 || (cSectorsPerCluster <= 128 && RT_IS_POWER_OF_TWO(cSectorsPerCluster)),
4637 ("cSectorsPerCluster=%#x\n", cSectorsPerCluster), VERR_INVALID_PARAMETER);
4638 if (bMedia != 0)
4639 {
4640 AssertMsgReturn(FAT_ID_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
4641 AssertMsgReturn(FATBPB_MEDIA_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
4642 }
4643 AssertReturn(!(fFlags & ~RTFSFATVOL_FMT_F_VALID_MASK), VERR_INVALID_FLAGS);
4644 AssertReturn(enmFatType >= RTFSFATTYPE_INVALID && enmFatType < RTFSFATTYPE_END, VERR_INVALID_PARAMETER);
4645
4646 if (!cbVol)
4647 {
4648 uint64_t cbFile;
4649 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
4650 AssertRCReturn(rc, rc);
4651 AssertMsgReturn(cbFile > offVol, ("cbFile=%#RX64 offVol=%#RX64\n", cbFile, offVol), VERR_INVALID_PARAMETER);
4652 cbVol = cbFile - offVol;
4653 }
4654 uint64_t const cSectorsInVol = cbVol / cbSector;
4655
4656 /*
4657 * Guess defaults if necessary.
4658 */
4659 if (!cSectorsPerCluster || !cHeads || !cSectorsPerTrack || !bMedia || !cRootDirEntries)
4660 {
4661 static struct
4662 {
4663 uint64_t cbVol;
4664 uint8_t bMedia;
4665 uint8_t cHeads;
4666 uint8_t cSectorsPerTrack;
4667 uint8_t cSectorsPerCluster;
4668 uint16_t cRootDirEntries;
4669 } s_aDefaults[] =
4670 {
4671 /* cbVol, bMedia, cHeads, cSectorsPTrk, cSectorsPClstr, cRootDirEntries */
4672 { 163840, 0xfe, /* cyl: 40,*/ 1, 8, 1, 64 },
4673 { 184320, 0xfc, /* cyl: 40,*/ 1, 9, 2, 64 },
4674 { 327680, 0xff, /* cyl: 40,*/ 2, 8, 2, 112 },
4675 { 368640, 0xfd, /* cyl: 40,*/ 2, 9, 2, 112 },
4676 { 737280, 0xf9, /* cyl: 80,*/ 2, 9, 2, 112 },
4677 { 1228800, 0xf9, /* cyl: 80,*/ 2, 15, 2, 112 },
4678 { 1474560, 0xf0, /* cyl: 80,*/ 2, 18, 1, 224 },
4679 { 2949120, 0xf0, /* cyl: 80,*/ 2, 36, 2, 224 },
4680 { 528482304, 0xf8, /* cyl: 1024,*/ 16, 63, 0, 512 }, // 504MB limit
4681 { UINT64_C(7927234560), 0xf8, /* cyl: 1024,*/ 240, 63, 0, 512 }, // 7.3GB limit
4682 { UINT64_C(8422686720), 0xf8, /* cyl: 1024,*/ 255, 63, 0, 512 }, // 7.84GB limit
4683
4684 };
4685 uint32_t iDefault = 0;
4686 while ( iDefault < RT_ELEMENTS(s_aDefaults) - 1U
4687 && cbVol > s_aDefaults[iDefault].cbVol)
4688 iDefault++;
4689 if (!cHeads)
4690 cHeads = s_aDefaults[iDefault].cHeads;
4691 if (!cSectorsPerTrack)
4692 cSectorsPerTrack = s_aDefaults[iDefault].cSectorsPerTrack;
4693 if (!bMedia)
4694 bMedia = s_aDefaults[iDefault].bMedia;
4695 if (!cRootDirEntries)
4696 cRootDirEntries = s_aDefaults[iDefault].cRootDirEntries;
4697 if (!cSectorsPerCluster)
4698 {
4699 cSectorsPerCluster = s_aDefaults[iDefault].cSectorsPerCluster;
4700 if (!cSectorsPerCluster)
4701 {
4702 uint32_t cbFat12Overhead = cbSector /* boot sector */
4703 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
4704 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4705 uint32_t cbFat16Overhead = cbSector /* boot sector */
4706 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
4707 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4708
4709 if ( enmFatType == RTFSFATTYPE_FAT12
4710 || cbVol <= cbFat12Overhead + FAT_MAX_FAT12_DATA_CLUSTERS * 4 * cbSector)
4711 {
4712 enmFatType = RTFSFATTYPE_FAT12;
4713 cSectorsPerCluster = 1;
4714 while ( cSectorsPerCluster < 128
4715 && cSectorsInVol
4716 > cbFat12Overhead / cbSector
4717 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT12_DATA_CLUSTERS
4718 + cSectorsPerCluster - 1)
4719 cSectorsPerCluster <<= 1;
4720 }
4721 else if ( enmFatType == RTFSFATTYPE_FAT16
4722 || cbVol <= cbFat16Overhead + FAT_MAX_FAT16_DATA_CLUSTERS * 128 * cbSector)
4723 {
4724 enmFatType = RTFSFATTYPE_FAT16;
4725 cSectorsPerCluster = 1;
4726 while ( cSectorsPerCluster < 128
4727 && cSectorsInVol
4728 > cbFat12Overhead / cbSector
4729 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT16_DATA_CLUSTERS
4730 + cSectorsPerCluster - 1)
4731 cSectorsPerCluster <<= 1;
4732 }
4733 else
4734 {
4735 /* The target here is keeping the FAT size below 8MB. Seems windows
4736 likes a minimum 4KB cluster size as wells as a max of 32KB (googling). */
4737 enmFatType = RTFSFATTYPE_FAT32;
4738 uint32_t cbFat32Overhead = cbSector * 32 /* boot sector, info sector, boot sector copies, reserved sectors */
4739 + _8M * cFats;
4740 if (cbSector >= _4K)
4741 cSectorsPerCluster = 1;
4742 else
4743 cSectorsPerCluster = _4K / cbSector;
4744 while ( cSectorsPerCluster < 128
4745 && cSectorsPerCluster * cbSector < _32K
4746 && cSectorsInVol > cbFat32Overhead / cbSector + (uint64_t)cSectorsPerCluster * _2M)
4747 cSectorsPerCluster <<= 1;
4748 }
4749 }
4750 }
4751 }
4752 Assert(cSectorsPerCluster);
4753 Assert(cRootDirEntries);
4754 uint32_t cbRootDir = RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector);
4755 uint32_t const cbCluster = cSectorsPerCluster * cbSector;
4756
4757 /*
4758 * If we haven't figured out the FAT type yet, do so.
4759 * The file system code determins the FAT based on cluster counts,
4760 * so we must do so here too.
4761 */
4762 if (enmFatType == RTFSFATTYPE_INVALID)
4763 {
4764 uint32_t cbFat12Overhead = cbSector /* boot sector */
4765 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
4766 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4767 if ( cbVol <= cbFat12Overhead + cbCluster
4768 || (cbVol - cbFat12Overhead) / cbCluster <= FAT_MAX_FAT12_DATA_CLUSTERS)
4769 enmFatType = RTFSFATTYPE_FAT12;
4770 else
4771 {
4772 uint32_t cbFat16Overhead = cbSector /* boot sector */
4773 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
4774 + cbRootDir;
4775 if ( cbVol <= cbFat16Overhead + cbCluster
4776 || (cbVol - cbFat16Overhead) / cbCluster <= FAT_MAX_FAT16_DATA_CLUSTERS)
4777 enmFatType = RTFSFATTYPE_FAT16;
4778 else
4779 enmFatType = RTFSFATTYPE_FAT32;
4780 }
4781 }
4782 if (enmFatType == RTFSFATTYPE_FAT32)
4783 cbRootDir = cbCluster;
4784
4785 /*
4786 * Calculate the FAT size and number of data cluster.
4787 *
4788 * Since the FAT size depends on how many data clusters there are, we start
4789 * with a minimum FAT size and maximum clust count, then recalucate it. The
4790 * result isn't necessarily stable, so we will only retry stabalizing the
4791 * result a few times.
4792 */
4793 uint32_t cbReservedFixed = enmFatType == RTFSFATTYPE_FAT32 ? 32 * cbSector : cbSector + cbRootDir;
4794 uint32_t cbFat = cbSector;
4795 if (cbReservedFixed + cbFat * cFats >= cbVol)
4796 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
4797 cbVol, cbReservedFixed, cbFat, cFats);
4798 uint32_t cMaxClusters = enmFatType == RTFSFATTYPE_FAT12 ? FAT_MAX_FAT12_DATA_CLUSTERS
4799 : enmFatType == RTFSFATTYPE_FAT16 ? FAT_MAX_FAT16_DATA_CLUSTERS
4800 : FAT_MAX_FAT12_DATA_CLUSTERS;
4801 uint32_t cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
4802 uint32_t cPrevClusters;
4803 uint32_t cTries = 4;
4804 do
4805 {
4806 cPrevClusters = cClusters;
4807 switch (enmFatType)
4808 {
4809 case RTFSFATTYPE_FAT12:
4810 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT12_TOTAL_CLUSTERS, cClusters) * 3 / 2;
4811 break;
4812 case RTFSFATTYPE_FAT16:
4813 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT16_TOTAL_CLUSTERS, cClusters) * 2;
4814 break;
4815 case RTFSFATTYPE_FAT32:
4816 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT32_TOTAL_CLUSTERS, cClusters) * 4;
4817 cbFat = RT_ALIGN_32(cbFat, _4K);
4818 break;
4819 default:
4820 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4821 }
4822 cbFat = RT_ALIGN_32(cbFat, cbSector);
4823 if (cbReservedFixed + cbFat * cFats >= cbVol)
4824 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
4825 cbVol, cbReservedFixed, cbFat, cFats);
4826 cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
4827 } while ( cClusters != cPrevClusters
4828 && cTries-- > 0);
4829 uint64_t const cTotalSectors = cClusters * (uint64_t)cSectorsPerCluster + (cbReservedFixed + cbFat * cFats) / cbSector;
4830
4831 /*
4832 * Check that the file system type and cluster count matches up. If they
4833 * don't the type will be misdetected.
4834 *
4835 * Note! These assertions could trigger if the above calculations are wrong.
4836 */
4837 switch (enmFatType)
4838 {
4839 case RTFSFATTYPE_FAT12:
4840 AssertMsgReturn(cClusters >= FAT_MIN_FAT12_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT12_DATA_CLUSTERS,
4841 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4842 break;
4843 case RTFSFATTYPE_FAT16:
4844 AssertMsgReturn(cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT16_DATA_CLUSTERS,
4845 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4846 break;
4847 case RTFSFATTYPE_FAT32:
4848 AssertMsgReturn(cClusters >= FAT_MIN_FAT32_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS,
4849 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4850 default:
4851 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4852 }
4853
4854 /*
4855 * Okay, create the boot sector.
4856 */
4857 size_t cbBuf = RT_MAX(RT_MAX(_64K, cbCluster), cbSector * 2U);
4858 uint8_t *pbBuf = (uint8_t *)RTMemTmpAllocZ(cbBuf);
4859 AssertReturn(pbBuf, VERR_NO_TMP_MEMORY);
4860
4861 const char *pszLastOp = "boot sector";
4862 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pbBuf;
4863 pBootSector->abJmp[0] = 0xeb;
4864 pBootSector->abJmp[1] = RT_UOFFSETOF(FATBOOTSECTOR, Bpb)
4865 + (enmFatType == RTFSFATTYPE_FAT32 ? sizeof(FAT32EBPB) : sizeof(FATEBPB)) - 2;
4866 pBootSector->abJmp[2] = 0x90;
4867 memcpy(pBootSector->achOemName, enmFatType == RTFSFATTYPE_FAT32 ? "FAT32 " : "IPRT 6.2", sizeof(pBootSector->achOemName));
4868 pBootSector->Bpb.Bpb331.cbSector = (uint16_t)cbSector;
4869 pBootSector->Bpb.Bpb331.cSectorsPerCluster = (uint8_t)cSectorsPerCluster;
4870 pBootSector->Bpb.Bpb331.cReservedSectors = enmFatType == RTFSFATTYPE_FAT32 ? cbReservedFixed / cbSector : 1;
4871 pBootSector->Bpb.Bpb331.cFats = (uint8_t)cFats;
4872 pBootSector->Bpb.Bpb331.cMaxRootDirEntries = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cRootDirEntries;
4873 pBootSector->Bpb.Bpb331.cTotalSectors16 = cTotalSectors <= UINT16_MAX ? (uint16_t)cTotalSectors : 0;
4874 pBootSector->Bpb.Bpb331.bMedia = bMedia;
4875 pBootSector->Bpb.Bpb331.cSectorsPerFat = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cbFat / cbSector;
4876 pBootSector->Bpb.Bpb331.cSectorsPerTrack = cSectorsPerTrack;
4877 pBootSector->Bpb.Bpb331.cTracksPerCylinder = cHeads;
4878 pBootSector->Bpb.Bpb331.cHiddenSectors = cHiddenSectors;
4879 pBootSector->Bpb.Bpb331.cTotalSectors32 = cTotalSectors <= UINT32_MAX ? (uint32_t)cTotalSectors : 0;
4880 if (enmFatType != RTFSFATTYPE_FAT32)
4881 {
4882 pBootSector->Bpb.Ebpb.bInt13Drive = 0;
4883 pBootSector->Bpb.Ebpb.bReserved = 0;
4884 pBootSector->Bpb.Ebpb.bExtSignature = FATEBPB_SIGNATURE;
4885 pBootSector->Bpb.Ebpb.uSerialNumber = RTRandU32();
4886 memset(pBootSector->Bpb.Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Ebpb.achLabel));
4887 memcpy(pBootSector->Bpb.Ebpb.achType, enmFatType == RTFSFATTYPE_FAT12 ? "FAT12 " : "FAT16 ",
4888 sizeof(pBootSector->Bpb.Ebpb.achType));
4889 }
4890 else
4891 {
4892 pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 = cbFat / cbSector;
4893 pBootSector->Bpb.Fat32Ebpb.fFlags = 0;
4894 pBootSector->Bpb.Fat32Ebpb.uVersion = FAT32EBPB_VERSION_0_0;
4895 pBootSector->Bpb.Fat32Ebpb.uRootDirCluster = FAT_FIRST_DATA_CLUSTER;
4896 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo = 1;
4897 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo = 6;
4898 RT_ZERO(pBootSector->Bpb.Fat32Ebpb.abReserved);
4899
4900 pBootSector->Bpb.Fat32Ebpb.bInt13Drive = 0;
4901 pBootSector->Bpb.Fat32Ebpb.bReserved = 0;
4902 pBootSector->Bpb.Fat32Ebpb.bExtSignature = FATEBPB_SIGNATURE;
4903 pBootSector->Bpb.Fat32Ebpb.uSerialNumber = RTRandU32();
4904 memset(pBootSector->Bpb.Fat32Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Fat32Ebpb.achLabel));
4905 if (cTotalSectors > UINT32_MAX)
4906 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 = cTotalSectors;
4907 else
4908 memcpy(pBootSector->Bpb.Fat32Ebpb.u.achType, "FAT32 ", sizeof(pBootSector->Bpb.Fat32Ebpb.u.achType));
4909 }
4910 pbBuf[pBootSector->abJmp[1] + 2 + 0] = 0xcd; /* int 19h */
4911 pbBuf[pBootSector->abJmp[1] + 2 + 1] = 0x19;
4912 pbBuf[pBootSector->abJmp[1] + 2 + 2] = 0xcc; /* int3 */
4913 pbBuf[pBootSector->abJmp[1] + 2 + 3] = 0xcc;
4914
4915 pBootSector->uSignature = FATBOOTSECTOR_SIGNATURE;
4916 if (cbSector != sizeof(*pBootSector))
4917 *(uint16_t *)&pbBuf[cbSector - 2] = FATBOOTSECTOR_SIGNATURE; /** @todo figure out how disks with non-512 byte sectors work! */
4918
4919 rc = RTVfsFileWriteAt(hVfsFile, offVol, pBootSector, cbSector, NULL);
4920 uint32_t const offFirstFat = pBootSector->Bpb.Bpb331.cReservedSectors * cbSector;
4921
4922 /*
4923 * Write the FAT32 info sector, 3 boot sector copies, and zero fill
4924 * the other reserved sectors.
4925 */
4926 if (RT_SUCCESS(rc) && enmFatType == RTFSFATTYPE_FAT32)
4927 {
4928 pszLastOp = "fat32 info sector";
4929 PFAT32INFOSECTOR pInfoSector = (PFAT32INFOSECTOR)&pbBuf[cbSector]; /* preserve the boot sector. */
4930 RT_ZERO(*pInfoSector);
4931 pInfoSector->uSignature1 = FAT32INFOSECTOR_SIGNATURE_1;
4932 pInfoSector->uSignature2 = FAT32INFOSECTOR_SIGNATURE_2;
4933 pInfoSector->uSignature3 = FAT32INFOSECTOR_SIGNATURE_3;
4934 pInfoSector->cFreeClusters = cClusters - 1; /* ASSUMES 1 cluster for the root dir. */
4935 pInfoSector->cLastAllocatedCluster = FAT_FIRST_DATA_CLUSTER;
4936 rc = RTVfsFileWriteAt(hVfsFile, offVol + cbSector, pInfoSector, cbSector, NULL);
4937
4938 uint32_t iSector = 2;
4939 if (RT_SUCCESS(rc))
4940 {
4941 pszLastOp = "fat32 unused reserved sectors";
4942 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
4943 (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo - iSector) * cbSector);
4944 iSector = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo;
4945 }
4946
4947 if (RT_SUCCESS(rc))
4948 {
4949 pszLastOp = "boot sector copy";
4950 for (uint32_t i = 0; i < 3 && RT_SUCCESS(rc); i++, iSector++)
4951 rc = RTVfsFileWriteAt(hVfsFile, offVol + iSector * cbSector, pBootSector, cbSector, NULL);
4952 }
4953
4954 if (RT_SUCCESS(rc))
4955 {
4956 pszLastOp = "fat32 unused reserved sectors";
4957 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
4958 (pBootSector->Bpb.Bpb331.cReservedSectors - iSector) * cbSector);
4959 }
4960 }
4961
4962 /*
4963 * The FATs.
4964 */
4965 if (RT_SUCCESS(rc))
4966 {
4967 pszLastOp = "fat";
4968 pBootSector = NULL; /* invalid */
4969 RT_BZERO(pbBuf, cbSector);
4970 switch (enmFatType)
4971 {
4972 case RTFSFATTYPE_FAT32:
4973 pbBuf[11] = 0x0f; /* EOC for root dir*/
4974 pbBuf[10] = 0xff;
4975 pbBuf[9] = 0xff;
4976 pbBuf[8] = 0xff;
4977 pbBuf[7] = 0x0f; /* Formatter's EOC, followed by signed extend FAT ID. */
4978 pbBuf[6] = 0xff;
4979 pbBuf[5] = 0xff;
4980 pbBuf[4] = 0xff;
4981 /* fall thru */
4982 case RTFSFATTYPE_FAT16:
4983 pbBuf[3] = 0xff;
4984 /* fall thru */
4985 case RTFSFATTYPE_FAT12:
4986 pbBuf[2] = 0xff;
4987 pbBuf[1] = 0xff;
4988 pbBuf[0] = bMedia; /* FAT ID */
4989 break;
4990 default: AssertFailed();
4991 }
4992 for (uint32_t iFatCopy = 0; iFatCopy < cFats && RT_SUCCESS(rc); iFatCopy++)
4993 {
4994 rc = RTVfsFileWriteAt(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy, pbBuf, cbSector, NULL);
4995 if (RT_SUCCESS(rc) && cbFat > cbSector)
4996 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy + cbSector, cbFat - cbSector);
4997 }
4998 }
4999
5000 /*
5001 * The root directory.
5002 */
5003 if (RT_SUCCESS(rc))
5004 {
5005 /** @todo any mandatory directory entries we need to fill in here? */
5006 pszLastOp = "root dir";
5007 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * cFats, cbRootDir);
5008 }
5009
5010 /*
5011 * If long format, fill the rest of the disk with 0xf6.
5012 */
5013 AssertCompile(RTFSFATVOL_FMT_F_QUICK != 0);
5014 if (RT_SUCCESS(rc) && !(fFlags & RTFSFATVOL_FMT_F_QUICK))
5015 {
5016 pszLastOp = "formatting data clusters";
5017 uint64_t offCur = offFirstFat + cbFat * cFats + cbRootDir;
5018 uint64_t cbLeft = cTotalSectors * cbSector;
5019 if (cbLeft > offCur)
5020 {
5021 cbLeft -= offCur;
5022 offCur += offVol;
5023
5024 memset(pbBuf, 0xf6, cbBuf);
5025 while (cbLeft > 0)
5026 {
5027 size_t cbToWrite = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
5028 rc = RTVfsFileWriteAt(hVfsFile, offCur, pbBuf, cbToWrite, NULL);
5029 if (RT_SUCCESS(rc))
5030 {
5031 offCur += cbToWrite;
5032 cbLeft -= cbToWrite;
5033 }
5034 else
5035 break;
5036 }
5037 }
5038 }
5039
5040 /*
5041 * Done.
5042 */
5043 RTMemTmpFree(pbBuf);
5044 if (RT_SUCCESS(rc))
5045 return rc;
5046 return RTErrInfoSet(pErrInfo, rc, pszLastOp);
5047}
5048
5049
5050/**
5051 * Formats a 1.44MB floppy image.
5052 *
5053 * @returns IPRT status code.
5054 * @param hVfsFile The image.
5055 */
5056RTDECL(int) RTFsFatVolFormat144(RTVFSFILE hVfsFile, bool fQuick)
5057{
5058 return RTFsFatVolFormat(hVfsFile, 0 /*offVol*/, 1474560, fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
5059 512 /*cbSector*/, 2 /*cSectorsPerCluster*/, RTFSFATTYPE_FAT12, 2 /*cHeads*/, 18 /*cSectors*/,
5060 0xf0 /*bMedia*/, 0 /*cHiddenSectors*/, 224 /*cRootDirEntries*/, NULL /*pErrInfo*/);
5061}
5062
5063
5064/**
5065 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
5066 */
5067static DECLCALLBACK(int) rtVfsChainFatVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
5068 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
5069{
5070 RT_NOREF(pProviderReg);
5071
5072 /*
5073 * Basic checks.
5074 */
5075 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
5076 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
5077 if ( pElement->enmType != RTVFSOBJTYPE_VFS
5078 && pElement->enmType != RTVFSOBJTYPE_DIR)
5079 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
5080 if (pElement->cArgs > 1)
5081 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
5082
5083 /*
5084 * Parse the flag if present, save in pElement->uProvider.
5085 */
5086 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
5087 if (pElement->cArgs > 0)
5088 {
5089 const char *psz = pElement->paArgs[0].psz;
5090 if (*psz)
5091 {
5092 if (!strcmp(psz, "ro"))
5093 fReadOnly = true;
5094 else if (!strcmp(psz, "rw"))
5095 fReadOnly = false;
5096 else
5097 {
5098 *poffError = pElement->paArgs[0].offSpec;
5099 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
5100 }
5101 }
5102 }
5103
5104 pElement->uProvider = fReadOnly;
5105 return VINF_SUCCESS;
5106}
5107
5108
5109/**
5110 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
5111 */
5112static DECLCALLBACK(int) rtVfsChainFatVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
5113 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
5114 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
5115{
5116 RT_NOREF(pProviderReg, pSpec, poffError);
5117
5118 int rc;
5119 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
5120 if (hVfsFileIn != NIL_RTVFSFILE)
5121 {
5122 RTVFS hVfs;
5123 rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, 0, &hVfs, pErrInfo);
5124 RTVfsFileRelease(hVfsFileIn);
5125 if (RT_SUCCESS(rc))
5126 {
5127 *phVfsObj = RTVfsObjFromVfs(hVfs);
5128 RTVfsRelease(hVfs);
5129 if (*phVfsObj != NIL_RTVFSOBJ)
5130 return VINF_SUCCESS;
5131 rc = VERR_VFS_CHAIN_CAST_FAILED;
5132 }
5133 }
5134 else
5135 rc = VERR_VFS_CHAIN_CAST_FAILED;
5136 return rc;
5137}
5138
5139
5140/**
5141 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
5142 */
5143static DECLCALLBACK(bool) rtVfsChainFatVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
5144 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
5145 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
5146{
5147 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
5148 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
5149 || !pReuseElement->paArgs[0].uProvider)
5150 return true;
5151 return false;
5152}
5153
5154
5155/** VFS chain element 'file'. */
5156static RTVFSCHAINELEMENTREG g_rtVfsChainFatVolReg =
5157{
5158 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
5159 /* fReserved = */ 0,
5160 /* pszName = */ "fat",
5161 /* ListEntry = */ { NULL, NULL },
5162 /* pszHelp = */ "Open a FAT file system, requires a file object on the left side.\n"
5163 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
5164 /* pfnValidate = */ rtVfsChainFatVol_Validate,
5165 /* pfnInstantiate = */ rtVfsChainFatVol_Instantiate,
5166 /* pfnCanReuseElement = */ rtVfsChainFatVol_CanReuseElement,
5167 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
5168};
5169
5170RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainFatVolReg, rtVfsChainFatVolReg);
5171
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