VirtualBox

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

Last change on this file since 68918 was 67878, checked in by vboxsync, 7 years ago

fatvfs.cpp: Fixed another formatting problem causing trouble with XP: Either set cTotalSectors16 or cTotalSectors32, never both. XP would ignore our WINNT.SIF file otherwise, while having no trouble accessing the floppy and files from the (rescue) command line. Also fixed a file date issue where we dated everything back to 1980.

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

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