VirtualBox

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

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

fatvfs.cpp: Return rc rather than VINF_SUCCESS in rtFsFatFile_Read & rtFsFatFile_Write. Duh!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 192.8 KB
Line 
1/* $Id: fatvfs.cpp 67302 2017-06-08 14:02:51Z vboxsync $ */
2/** @file
3 * IPRT - FAT Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#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 = ((RT_MIN(Time.i32Year, 1980) - 1980) << 9)
1419 | (Time.u8Month << 5)
1420 | Time.u8MonthDay;
1421 if (puTime)
1422 *puTime = (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, 12+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), FATDIRNAMESLOT_CHARS_PER_SLOT));
2991
2992 uint8_t const bChecksum = rtFsFatDir_CalcChecksum(pDirEntry);
2993 size_t const cSlots = (cwcEntry + FATDIRNAMESLOT_CHARS_PER_SLOT - 1) / FATDIRNAMESLOT_CHARS_PER_SLOT;
2994 size_t iSlot = cSlots;
2995 PCRTUTF16 pwszSrc = wszEntry;
2996 while (iSlot-- > 0)
2997 {
2998 memcpy(paSlots[iSlot].awcName0, pwszSrc, sizeof(paSlots[iSlot].awcName0));
2999 pwszSrc += RT_ELEMENTS(paSlots[iSlot].awcName0);
3000 memcpy(paSlots[iSlot].awcName1, pwszSrc, sizeof(paSlots[iSlot].awcName1));
3001 pwszSrc += RT_ELEMENTS(paSlots[iSlot].awcName1);
3002 memcpy(paSlots[iSlot].awcName2, pwszSrc, sizeof(paSlots[iSlot].awcName2));
3003 pwszSrc += RT_ELEMENTS(paSlots[iSlot].awcName2);
3004
3005 paSlots[iSlot].idSlot = (uint8_t)(cSlots - iSlot);
3006 paSlots[iSlot].fAttrib = FAT_ATTR_NAME_SLOT;
3007 paSlots[iSlot].fZero = 0;
3008 paSlots[iSlot].idxZero = 0;
3009 paSlots[iSlot].bChecksum = bChecksum;
3010 }
3011 paSlots[0].idSlot |= FATDIRNAMESLOT_FIRST_SLOT_FLAG;
3012 *pcSlots = (uint32_t)cSlots;
3013 return VINF_SUCCESS;
3014 }
3015 }
3016 *pcSlots = UINT32_MAX;
3017 return rc;
3018}
3019
3020
3021/**
3022 * Searches the directory for a given number of free directory entries.
3023 *
3024 * The free entries must be consecutive of course.
3025 *
3026 * @returns IPRT status code.
3027 * @retval VERR_DISK_FULL if no space was found, *pcFreeTail set.
3028 * @param pThis The directory to search.
3029 * @param cEntriesNeeded How many entries we need.
3030 * @param poffEntryInDir Where to return the offset of the first entry we
3031 * found.
3032 * @param pcFreeTail Where to return the number of free entries at the
3033 * end of the directory when VERR_DISK_FULL is
3034 * returned.
3035 */
3036static int rtFsFatChain_FindFreeEntries(PRTFSFATDIR pThis, uint32_t cEntriesNeeded,
3037 uint32_t *poffEntryInDir, uint32_t *pcFreeTail)
3038{
3039 /* First try make gcc happy. */
3040 *pcFreeTail = 0;
3041 *poffEntryInDir = UINT32_MAX;
3042
3043 /*
3044 * Scan the whole directory, buffer by buffer.
3045 */
3046 uint32_t offStartFreeEntries = UINT32_MAX;
3047 uint32_t cFreeEntries = 0;
3048 uint32_t offEntryInDir = 0;
3049 uint32_t const cbDir = pThis->Core.cbObject;
3050 Assert(RT_ALIGN_32(cbDir, sizeof(FATDIRENTRY)) == cbDir);
3051 while (offEntryInDir < cbDir)
3052 {
3053 /* Get chunk of entries starting at offEntryInDir. */
3054 uint32_t uBufferLock = UINT32_MAX;
3055 uint32_t cEntries = 0;
3056 PCFATDIRENTRYUNION paEntries = NULL;
3057 int rc = rtFsFatDir_GetEntriesAt(pThis, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
3058 if (RT_FAILURE(rc))
3059 return rc;
3060
3061 /*
3062 * Now work thru each of the entries.
3063 */
3064 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
3065 {
3066 uint8_t const bFirst = paEntries[iEntry].Entry.achName[0];
3067 if ( bFirst == FATDIRENTRY_CH0_DELETED
3068 || bFirst == FATDIRENTRY_CH0_END_OF_DIR)
3069 {
3070 if (offStartFreeEntries != UINT32_MAX)
3071 cFreeEntries++;
3072 else
3073 {
3074 offStartFreeEntries = offEntryInDir;
3075 cFreeEntries = 1;
3076 }
3077 if (cFreeEntries >= cEntriesNeeded)
3078 {
3079 *pcFreeTail = cEntriesNeeded;
3080 *poffEntryInDir = offStartFreeEntries;
3081 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
3082 return VINF_SUCCESS;
3083 }
3084
3085 if (bFirst == FATDIRENTRY_CH0_END_OF_DIR)
3086 {
3087 if (pThis->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
3088 {
3089 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
3090 *pcFreeTail = cFreeEntries = (cbDir - offStartFreeEntries) / sizeof(FATDIRENTRY);
3091 if (cFreeEntries >= cEntriesNeeded)
3092 {
3093 *poffEntryInDir = offStartFreeEntries;
3094 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
3095 return VINF_SUCCESS;
3096 }
3097 return VERR_DISK_FULL;
3098 }
3099 }
3100 }
3101 else if (offStartFreeEntries != UINT32_MAX)
3102 {
3103 offStartFreeEntries = UINT32_MAX;
3104 cFreeEntries = 0;
3105 }
3106 }
3107 rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
3108 }
3109 *pcFreeTail = cFreeEntries;
3110 return VERR_DISK_FULL;
3111}
3112
3113
3114/**
3115 * Try grow the directory.
3116 *
3117 * This is not called on the root directory.
3118 *
3119 * @returns IPRT status code.
3120 * @retval VERR_DISK_FULL if we failed to allocated new space.
3121 * @param pThis The directory to grow.
3122 * @param cMinNewEntries The minimum number of new entries to allocated.
3123 */
3124static int rtFsFatChain_GrowDirectory(PRTFSFATDIR pThis, uint32_t cMinNewEntries)
3125{
3126 RT_NOREF(pThis, cMinNewEntries);
3127 return VERR_DISK_FULL;
3128}
3129
3130
3131/**
3132 * Inserts a directory with zero of more long name slots preceeding it.
3133 *
3134 * @returns IPRT status code.
3135 * @param pThis The directory.
3136 * @param pDirEntry The directory entry.
3137 * @param paSlots The long name slots.
3138 * @param cSlots The number of long name slots.
3139 * @param poffEntryInDir Where to return the directory offset.
3140 */
3141static int rtFsFatChain_InsertEntries(PRTFSFATDIR pThis, PCFATDIRENTRY pDirEntry, PFATDIRNAMESLOT paSlots, uint32_t cSlots,
3142 uint32_t *poffEntryInDir)
3143{
3144 uint32_t const cTotalEntries = cSlots + 1;
3145
3146 /*
3147 * Find somewhere to put the entries. Try extend the directory if we're
3148 * not successful at first.
3149 */
3150 uint32_t cFreeTailEntries;
3151 uint32_t offFirstInDir;
3152 int rc = rtFsFatChain_FindFreeEntries(pThis, cTotalEntries, &offFirstInDir, &cFreeTailEntries);
3153 if (rc == VERR_DISK_FULL)
3154 {
3155 Assert(cFreeTailEntries < cTotalEntries);
3156
3157 /* Try grow it and use the newly allocated space. */
3158 if ( pThis->Core.pParentDir
3159 && pThis->cEntries < _64K /* Don't grow beyond 64K entries */)
3160 {
3161 offFirstInDir = pThis->Core.cbObject - cFreeTailEntries * sizeof(FATDIRENTRY);
3162 rc = rtFsFatChain_GrowDirectory(pThis, cTotalEntries - cFreeTailEntries);
3163 }
3164
3165 if (rc == VERR_DISK_FULL)
3166 {
3167 /** @todo Try compact the directory if we couldn't grow it. */
3168 }
3169 }
3170 if (RT_SUCCESS(rc))
3171 {
3172 /*
3173 * Update the directory.
3174 */
3175 uint32_t offCurrent = offFirstInDir;
3176 for (uint32_t iSrcSlot = 0; iSrcSlot < cTotalEntries; iSrcSlot++, offCurrent += sizeof(FATDIRENTRY))
3177 {
3178 uint32_t uBufferLock;
3179 PFATDIRENTRY pDstEntry;
3180 rc = rtFsFatDir_GetEntryForUpdate(pThis, offCurrent, &pDstEntry, &uBufferLock);
3181 if (RT_SUCCESS(rc))
3182 {
3183 if (iSrcSlot < cSlots)
3184 memcpy(pDstEntry, &paSlots[iSrcSlot], sizeof(*pDstEntry));
3185 else
3186 memcpy(pDstEntry, pDirEntry, sizeof(*pDstEntry));
3187 rc = rtFsFatDir_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
3188 if (RT_SUCCESS(rc))
3189 continue;
3190
3191 /*
3192 * Bail out: Try mark any edited entries as deleted.
3193 */
3194 iSrcSlot++;
3195 }
3196 while (iSrcSlot-- > 0)
3197 {
3198 int rc2 = rtFsFatDir_GetEntryForUpdate(pThis, offFirstInDir + iSrcSlot * sizeof(FATDIRENTRY),
3199 &pDstEntry, &uBufferLock);
3200 if (RT_SUCCESS(rc2))
3201 {
3202 pDstEntry->achName[0] = FATDIRENTRY_CH0_DELETED;
3203 rtFsFatDir_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
3204 }
3205 }
3206 *poffEntryInDir = UINT32_MAX;
3207 return rc;
3208 }
3209 AssertRC(rc);
3210
3211 /*
3212 * Successfully inserted all.
3213 */
3214 *poffEntryInDir = offFirstInDir + cSlots * sizeof(FATDIRENTRY);
3215 return VINF_SUCCESS;
3216 }
3217
3218 *poffEntryInDir = UINT32_MAX;
3219 return rc;
3220}
3221
3222
3223
3224/**
3225 * Creates a new directory entry.
3226 *
3227 * @returns IPRT status code
3228 * @param pThis The directory.
3229 * @param pszEntry The name of the new entry.
3230 * @param fAttrib The attributes.
3231 * @param cbInitial The initialize size.
3232 * @param poffEntryInDir Where to return the offset of the directory entry.
3233 * @param pDirEntry Where to return a copy of the directory entry.
3234 *
3235 * @remarks ASSUMES caller has already called rtFsFatDir_FindEntry to make sure
3236 * the entry doesn't exist.
3237 */
3238static int rtFsFatDir_CreateEntry(PRTFSFATDIR pThis, const char *pszEntry, uint8_t fAttrib, uint32_t cbInitial,
3239 uint32_t *poffEntryInDir, PFATDIRENTRY pDirEntry)
3240{
3241 PRTFSFATVOL pVol = pThis->Core.pVol;
3242 *poffEntryInDir = UINT32_MAX;
3243 if (pVol->fReadOnly)
3244 return VERR_WRITE_PROTECT;
3245
3246 /*
3247 * Create the directory entries on the stack.
3248 */
3249 bool fIs8Dot3Name = rtFsFatDir_StringTo8Dot3((char *)pDirEntry->achName, pszEntry);
3250 pDirEntry->fAttrib = fAttrib;
3251 pDirEntry->fCase = fIs8Dot3Name ? rtFsFatDir_CalcCaseFlags(pszEntry) : 0;
3252 pDirEntry->uBirthCentiseconds = rtFsFatCurrentFatDateTime(pVol, &pDirEntry->uBirthDate, &pDirEntry->uBirthTime);
3253 pDirEntry->uAccessDate = pDirEntry->uBirthDate;
3254 pDirEntry->uModifyDate = pDirEntry->uBirthDate;
3255 pDirEntry->uModifyTime = pDirEntry->uBirthTime;
3256 pDirEntry->idxCluster = 0; /* Will fill this in later if cbInitial is non-zero. */
3257 pDirEntry->u.idxClusterHigh = 0;
3258 pDirEntry->cbFile = cbInitial;
3259
3260 /*
3261 * Create long filename slots if necessary.
3262 */
3263 uint32_t cSlots = UINT32_MAX;
3264 FATDIRNAMESLOT aSlots[FATDIRNAMESLOT_MAX_SLOTS];
3265 AssertCompile(RTFSFAT_MAX_LFN_CHARS < RT_ELEMENTS(aSlots) * FATDIRNAMESLOT_CHARS_PER_SLOT);
3266 int rc = rtFsFatDir_MaybeCreateLongNameAndShortAlias(pThis, pszEntry, fIs8Dot3Name, pDirEntry, aSlots, &cSlots);
3267 if (RT_SUCCESS(rc))
3268 {
3269 Assert(cSlots <= FATDIRNAMESLOT_MAX_SLOTS);
3270
3271 /*
3272 * Allocate initial clusters if requested.
3273 */
3274 RTFSFATCHAIN Clusters;
3275 rtFsFatChain_InitEmpty(&Clusters, pVol);
3276 if (cbInitial > 0)
3277 {
3278 rc = rtFsFatClusterMap_AllocateMoreClusters(pVol, &Clusters,
3279 (cbInitial + Clusters.cbCluster - 1) >> Clusters.cClusterByteShift);
3280 if (RT_SUCCESS(rc))
3281 {
3282 uint32_t idxFirstCluster = rtFsFatChain_GetFirstCluster(&Clusters);
3283 pDirEntry->idxCluster = (uint16_t)idxFirstCluster;
3284 if (pVol->enmFatType >= RTFSFATTYPE_FAT32)
3285 pDirEntry->u.idxClusterHigh = (uint16_t)(idxFirstCluster >> 16);
3286 }
3287 }
3288 if (RT_SUCCESS(rc))
3289 {
3290 /*
3291 * Insert the directory entry and name slots.
3292 */
3293 rc = rtFsFatChain_InsertEntries(pThis, pDirEntry, aSlots, cSlots, poffEntryInDir);
3294 if (RT_SUCCESS(rc))
3295 {
3296 rtFsFatChain_Delete(&Clusters);
3297 return VINF_SUCCESS;
3298 }
3299
3300 for (uint32_t iClusterToFree = 0; iClusterToFree < Clusters.cClusters; iClusterToFree++)
3301 rtFsFatClusterMap_FreeCluster(pVol, rtFsFatChain_GetClusterByIndex(&Clusters, iClusterToFree));
3302 rtFsFatChain_Delete(&Clusters);
3303 }
3304 }
3305 return rc;
3306}
3307
3308
3309
3310/**
3311 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
3312 */
3313static DECLCALLBACK(int) rtFsFatDir_Close(void *pvThis)
3314{
3315 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3316 int rc;
3317 if (pThis->paEntries)
3318 {
3319 rc = rtFsFatDir_Flush(pThis);
3320 RTMemFree(pThis->paEntries);
3321 pThis->paEntries = NULL;
3322 }
3323 else
3324 rc = VINF_SUCCESS;
3325
3326 rtFsFatObj_Close(&pThis->Core);
3327 return rc;
3328}
3329
3330
3331/**
3332 * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
3333 */
3334static DECLCALLBACK(int) rtFsFatDir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
3335 PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
3336{
3337 /*
3338 * FAT doesn't do symbolic links and mounting file systems within others
3339 * haven't been implemented yet, I think, so only care if a directory is
3340 * asked for.
3341 */
3342 int rc;
3343 if (phVfsSymlink)
3344 *phVfsSymlink = NIL_RTVFSSYMLINK;
3345 if (phVfsMounted)
3346 *phVfsMounted = NIL_RTVFS;
3347 if (phVfsDir)
3348 {
3349 *phVfsDir = NIL_RTVFSDIR;
3350
3351 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3352 uint32_t offEntryInDir;
3353 bool fLong;
3354 FATDIRENTRY DirEntry;
3355 rc = rtFsFatDir_FindEntry(pThis, pszEntry, &offEntryInDir, &fLong, &DirEntry);
3356 if (RT_SUCCESS(rc))
3357 {
3358 switch (DirEntry.fAttrib & (FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME))
3359 {
3360 case FAT_ATTR_DIRECTORY:
3361 {
3362 rc = rtFsFatDir_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir,
3363 RTFSFAT_GET_CLUSTER(&DirEntry, pThis->Core.pVol), UINT64_MAX /*offDisk*/,
3364 DirEntry.cbFile, phVfsDir, NULL /*ppDir*/);
3365 break;
3366 }
3367 case 0:
3368 rc = VERR_NOT_A_DIRECTORY;
3369 break;
3370 default:
3371 rc = VERR_PATH_NOT_FOUND;
3372 break;
3373 }
3374 }
3375 else if (rc == VERR_FILE_NOT_FOUND)
3376 rc = VERR_PATH_NOT_FOUND;
3377 }
3378 else
3379 rc = VERR_PATH_NOT_FOUND;
3380 return rc;
3381}
3382
3383
3384/**
3385 * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
3386 */
3387static DECLCALLBACK(int) rtFsFatDir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
3388{
3389 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3390
3391 /*
3392 * Try open existing file.
3393 */
3394 uint32_t offEntryInDir;
3395 bool fLong;
3396 FATDIRENTRY DirEntry;
3397 int rc = rtFsFatDir_FindEntry(pThis, pszFilename, &offEntryInDir, &fLong, &DirEntry);
3398 if (RT_SUCCESS(rc))
3399 {
3400 switch (DirEntry.fAttrib & (FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME))
3401 {
3402 case 0:
3403 if ( !(DirEntry.fAttrib & FAT_ATTR_READONLY)
3404 || !(fOpen & RTFILE_O_WRITE))
3405 {
3406 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
3407 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
3408 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
3409 rc = rtFsFatFile_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir, fOpen, phVfsFile);
3410 else
3411 rc = VERR_ALREADY_EXISTS;
3412 }
3413 else
3414 rc = VERR_ACCESS_DENIED;
3415 break;
3416
3417 case FAT_ATTR_DIRECTORY:
3418 rc = VERR_NOT_A_FILE;
3419 break;
3420 default:
3421 rc = VERR_PATH_NOT_FOUND;
3422 break;
3423 }
3424 }
3425 /*
3426 * Create the file?
3427 */
3428 else if ( rc == VERR_FILE_NOT_FOUND
3429 && ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
3430 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
3431 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE) )
3432 {
3433 rc = rtFsFatDir_CreateEntry(pThis, pszFilename, FAT_ATTR_ARCHIVE, 0 /*cbInitial*/, &offEntryInDir, &DirEntry);
3434 if (RT_SUCCESS(rc))
3435 rc = rtFsFatFile_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir, fOpen, phVfsFile);
3436 }
3437 return rc;
3438}
3439
3440
3441/**
3442 * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
3443 */
3444static DECLCALLBACK(int) rtFsFatDir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
3445{
3446 RT_NOREF(pvThis, pszSubDir, fFlags, phVfsDir);
3447RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSubDir);
3448 return VERR_NOT_IMPLEMENTED;
3449}
3450
3451
3452/**
3453 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3454 */
3455static DECLCALLBACK(int) rtFsFatDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3456{
3457 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3458RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSubDir);
3459 return VERR_NOT_IMPLEMENTED;
3460}
3461
3462
3463/**
3464 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3465 */
3466static DECLCALLBACK(int) rtFsFatDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3467{
3468 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3469RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
3470 return VERR_NOT_SUPPORTED;
3471}
3472
3473
3474/**
3475 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3476 */
3477static DECLCALLBACK(int) rtFsFatDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3478 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3479{
3480 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3481RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
3482 return VERR_NOT_SUPPORTED;
3483}
3484
3485
3486/**
3487 * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
3488 */
3489static DECLCALLBACK(int) rtFsFatDir_QueryEntryInfo(void *pvThis, const char *pszEntry,
3490 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3491{
3492 /*
3493 * Try locate the entry.
3494 */
3495 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3496 uint32_t offEntryInDir;
3497 bool fLong;
3498 FATDIRENTRY DirEntry;
3499 int rc = rtFsFatDir_FindEntry(pThis, pszEntry, &offEntryInDir, &fLong, &DirEntry);
3500 Log2(("rtFsFatDir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
3501 if (RT_SUCCESS(rc))
3502 {
3503 /*
3504 * To avoid duplicating code in rtFsFatObj_InitFromDirRec and
3505 * rtFsFatObj_QueryInfo, we create a dummy RTFSFATOBJ on the stack.
3506 */
3507 RTFSFATOBJ TmpObj;
3508 RT_ZERO(TmpObj);
3509 rtFsFatObj_InitFromDirEntry(&TmpObj, &DirEntry, offEntryInDir, pThis->Core.pVol);
3510 rc = rtFsFatObj_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
3511 }
3512 return rc;
3513}
3514
3515
3516/**
3517 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3518 */
3519static DECLCALLBACK(int) rtFsFatDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3520{
3521 RT_NOREF(pvThis, pszEntry, fType);
3522RTAssertMsg2("%s: %s\n", __FUNCTION__, pszEntry);
3523 return VERR_NOT_IMPLEMENTED;
3524}
3525
3526
3527/**
3528 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3529 */
3530static DECLCALLBACK(int) rtFsFatDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3531{
3532 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3533RTAssertMsg2("%s: %s\n", __FUNCTION__, pszEntry);
3534 return VERR_NOT_IMPLEMENTED;
3535}
3536
3537
3538/**
3539 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3540 */
3541static DECLCALLBACK(int) rtFsFatDir_RewindDir(void *pvThis)
3542{
3543 RT_NOREF(pvThis);
3544RTAssertMsg2("%s\n", __FUNCTION__);
3545 return VERR_NOT_IMPLEMENTED;
3546}
3547
3548
3549/**
3550 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3551 */
3552static DECLCALLBACK(int) rtFsFatDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3553 RTFSOBJATTRADD enmAddAttr)
3554{
3555 RT_NOREF(pvThis, pDirEntry, pcbDirEntry, enmAddAttr);
3556RTAssertMsg2("%s\n", __FUNCTION__);
3557 return VERR_NOT_IMPLEMENTED;
3558}
3559
3560
3561/**
3562 * FAT file operations.
3563 */
3564static const RTVFSDIROPS g_rtFsFatDirOps =
3565{
3566 { /* Obj */
3567 RTVFSOBJOPS_VERSION,
3568 RTVFSOBJTYPE_DIR,
3569 "FatDir",
3570 rtFsFatDir_Close,
3571 rtFsFatObj_QueryInfo,
3572 RTVFSOBJOPS_VERSION
3573 },
3574 RTVFSDIROPS_VERSION,
3575 0,
3576 { /* ObjSet */
3577 RTVFSOBJSETOPS_VERSION,
3578 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
3579 rtFsFatObj_SetMode,
3580 rtFsFatObj_SetTimes,
3581 rtFsFatObj_SetOwner,
3582 RTVFSOBJSETOPS_VERSION
3583 },
3584 rtFsFatDir_TraversalOpen,
3585 rtFsFatDir_OpenFile,
3586 rtFsFatDir_OpenDir,
3587 rtFsFatDir_CreateDir,
3588 rtFsFatDir_OpenSymlink,
3589 rtFsFatDir_CreateSymlink,
3590 rtFsFatDir_QueryEntryInfo,
3591 rtFsFatDir_UnlinkEntry,
3592 rtFsFatDir_RenameEntry,
3593 rtFsFatDir_RewindDir,
3594 rtFsFatDir_ReadDir,
3595 RTVFSDIROPS_VERSION,
3596};
3597
3598
3599/**
3600 * Adds an open child to the parent directory.
3601 *
3602 * Maintains an additional reference to the parent dir to prevent it from going
3603 * away. If @a pDir is the root directory, it also ensures the volume is
3604 * referenced and sticks around until the last open object is gone.
3605 *
3606 * @param pDir The directory.
3607 * @param pChild The child being opened.
3608 * @sa rtFsFatDir_RemoveOpenChild
3609 */
3610static void rtFsFatDir_AddOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild)
3611{
3612 /* First child that gets opened retains the parent directory. This is
3613 released by the final open child. */
3614 if (RTListIsEmpty(&pDir->OpenChildren))
3615 {
3616 uint32_t cRefs = RTVfsDirRetain(pDir->hVfsSelf);
3617 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3618
3619 /* Root also retains the whole file system. */
3620 if (pDir->Core.pVol->pRootDir == pDir)
3621 {
3622 Assert(pDir->Core.pVol);
3623 Assert(pDir->Core.pVol == pChild->pVol);
3624 cRefs = RTVfsRetain(pDir->Core.pVol->hVfsSelf);
3625 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3626 }
3627 }
3628 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
3629 pChild->pParentDir = pDir;
3630}
3631
3632
3633/**
3634 * Removes an open child to the parent directory.
3635 *
3636 * @param pDir The directory.
3637 * @param pChild The child being removed.
3638 *
3639 * @remarks This is the very last thing you do as it may cause a few other
3640 * objects to be released recursively (parent dir and the volume).
3641 *
3642 * @sa rtFsFatDir_AddOpenChild
3643 */
3644static void rtFsFatDir_RemoveOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild)
3645{
3646 AssertReturnVoid(pChild->pParentDir == pDir);
3647 RTListNodeRemove(&pChild->Entry);
3648 pChild->pParentDir = NULL;
3649
3650 /* Final child? If so, release directory. */
3651 if (RTListIsEmpty(&pDir->OpenChildren))
3652 {
3653 bool const fIsRootDir = pDir->Core.pVol->pRootDir == pDir;
3654
3655 uint32_t cRefs = RTVfsDirRelease(pDir->hVfsSelf);
3656 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3657
3658 /* Root directory releases the file system as well. Since the volume
3659 holds a reference to the root directory, it will remain valid after
3660 the above release. */
3661 if (fIsRootDir)
3662 {
3663 Assert(cRefs > 0);
3664 Assert(pDir->Core.pVol);
3665 Assert(pDir->Core.pVol == pChild->pVol);
3666 cRefs = RTVfsRelease(pDir->Core.pVol->hVfsSelf);
3667 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
3668 }
3669 }
3670}
3671
3672
3673/**
3674 * Instantiates a new directory.
3675 *
3676 * @returns IPRT status code.
3677 * @param pThis The FAT volume instance.
3678 * @param pParentDir The parent directory. This is NULL for the root
3679 * directory.
3680 * @param pDirEntry The parent directory entry. This is NULL for the
3681 * root directory.
3682 * @param offEntryInDir The byte offset of the directory entry in the parent
3683 * directory. UINT32_MAX if root directory.
3684 * @param idxCluster The cluster where the directory content is to be
3685 * found. This can be UINT32_MAX if a root FAT12/16
3686 * directory.
3687 * @param offDisk The disk byte offset of the FAT12/16 root directory.
3688 * This is UINT64_MAX if idxCluster is given.
3689 * @param cbDir The size of the directory.
3690 * @param phVfsDir Where to return the directory handle.
3691 * @param ppDir Where to return the FAT directory instance data.
3692 */
3693static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
3694 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir, PRTFSFATDIR *ppDir)
3695{
3696 Assert((idxCluster == UINT32_MAX) != (offDisk == UINT64_MAX));
3697 Assert((pDirEntry == NULL) == (offEntryInDir == UINT32_MAX));
3698 if (ppDir)
3699 *ppDir = NULL;
3700
3701 PRTFSFATDIR pNewDir;
3702 int rc = RTVfsNewDir(&g_rtFsFatDirOps, sizeof(*pNewDir), pParentDir ? 0 : RTVFSDIR_F_NO_VFS_REF,
3703 pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
3704 if (RT_SUCCESS(rc))
3705 {
3706 /*
3707 * Initialize it all so rtFsFatDir_Close doesn't trip up in anyway.
3708 */
3709 RTListInit(&pNewDir->OpenChildren);
3710 if (pDirEntry)
3711 rtFsFatObj_InitFromDirEntry(&pNewDir->Core, pDirEntry, offEntryInDir, pThis);
3712 else
3713 rtFsFatObj_InitDummy(&pNewDir->Core, cbDir, RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_UNIX_ALL_PERMS, pThis);
3714
3715 pNewDir->hVfsSelf = *phVfsDir;
3716 pNewDir->cEntries = cbDir / sizeof(FATDIRENTRY);
3717 pNewDir->fIsLinearRootDir = idxCluster == UINT32_MAX;
3718 pNewDir->fFullyBuffered = pNewDir->fIsLinearRootDir;
3719 pNewDir->paEntries = NULL;
3720 pNewDir->offEntriesOnDisk = UINT64_MAX;
3721 if (pNewDir->fFullyBuffered)
3722 pNewDir->cbAllocatedForEntries = RT_ALIGN_32(cbDir, pThis->cbSector);
3723 else
3724 pNewDir->cbAllocatedForEntries = pThis->cbSector;
3725
3726 /*
3727 * If clustered backing, read the chain and see if we cannot still do the full buffering.
3728 */
3729 if (idxCluster != UINT32_MAX)
3730 {
3731 rc = rtFsFatClusterMap_ReadClusterChain(pThis, idxCluster, &pNewDir->Core.Clusters);
3732 if (RT_SUCCESS(rc))
3733 {
3734 if ( pNewDir->Core.Clusters.cClusters >= 1
3735 && pNewDir->Core.Clusters.cbChain <= _64K
3736 && rtFsFatChain_IsContiguous(&pNewDir->Core.Clusters))
3737 {
3738 Assert(pNewDir->Core.Clusters.cbChain >= cbDir);
3739 pNewDir->cbAllocatedForEntries = pNewDir->Core.Clusters.cbChain;
3740 pNewDir->fFullyBuffered = true;
3741 }
3742 }
3743 }
3744 else
3745 rtFsFatChain_InitEmpty(&pNewDir->Core.Clusters, pThis);
3746 if (RT_SUCCESS(rc))
3747 {
3748 /*
3749 * Allocate and initialize the buffering. Fill the buffer.
3750 */
3751 pNewDir->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pNewDir->cbAllocatedForEntries);
3752 if (!pNewDir->paEntries)
3753 {
3754 if (pNewDir->fFullyBuffered && !pNewDir->fIsLinearRootDir)
3755 {
3756 pNewDir->fFullyBuffered = false;
3757 pNewDir->cbAllocatedForEntries = pThis->cbSector;
3758 pNewDir->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pNewDir->cbAllocatedForEntries);
3759 }
3760 if (!pNewDir->paEntries)
3761 rc = VERR_NO_MEMORY;
3762 }
3763
3764 if (RT_SUCCESS(rc))
3765 {
3766 if (pNewDir->fFullyBuffered)
3767 {
3768 pNewDir->u.Full.cDirtySectors = 0;
3769 pNewDir->u.Full.cSectors = pNewDir->cbAllocatedForEntries / pThis->cbSector;
3770 pNewDir->u.Full.pbDirtySectors = (uint8_t *)RTMemAllocZ((pNewDir->u.Full.cSectors + 63) / 8);
3771 if (pNewDir->u.Full.pbDirtySectors)
3772 pNewDir->offEntriesOnDisk = offDisk != UINT64_MAX ? offDisk
3773 : rtFsFatClusterToDiskOffset(pThis, idxCluster);
3774 else
3775 rc = VERR_NO_MEMORY;
3776 }
3777 else
3778 {
3779 pNewDir->offEntriesOnDisk = rtFsFatClusterToDiskOffset(pThis, idxCluster);
3780 pNewDir->u.Simple.offInDir = 0;
3781 pNewDir->u.Simple.fDirty = false;
3782 }
3783 if (RT_SUCCESS(rc))
3784 rc = RTVfsFileReadAt(pThis->hVfsBacking, pNewDir->offEntriesOnDisk,
3785 pNewDir->paEntries, pNewDir->cbAllocatedForEntries, NULL);
3786 if (RT_SUCCESS(rc))
3787 {
3788 /*
3789 * Link into parent directory so we can use it to update
3790 * our directory entry.
3791 */
3792 if (pParentDir)
3793 rtFsFatDir_AddOpenChild(pParentDir, &pNewDir->Core);
3794 if (ppDir)
3795 *ppDir = pNewDir;
3796 return VINF_SUCCESS;
3797 }
3798 }
3799
3800 /* Free the buffer on failure so rtFsFatDir_Close doesn't try do anything with it. */
3801 RTMemFree(pNewDir->paEntries);
3802 pNewDir->paEntries = NULL;
3803 }
3804
3805 RTVfsDirRelease(*phVfsDir);
3806 }
3807 *phVfsDir = NIL_RTVFSDIR;
3808 if (ppDir)
3809 *ppDir = NULL;
3810 return rc;
3811}
3812
3813
3814
3815
3816
3817/**
3818 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
3819 */
3820static DECLCALLBACK(int) rtFsFatVol_Close(void *pvThis)
3821{
3822 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
3823 LogFlow(("rtFsFatVol_Close(%p)\n", pThis));
3824 int rc = rtFsFatClusterMap_Destroy(pThis);
3825
3826 if (pThis->hVfsRootDir != NIL_RTVFSDIR)
3827 {
3828 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
3829 uint32_t cRefs = RTVfsDirRelease(pThis->hVfsRootDir);
3830 Assert(cRefs == 0); NOREF(cRefs);
3831 pThis->hVfsRootDir = NIL_RTVFSDIR;
3832 pThis->pRootDir = NULL;
3833 }
3834
3835 RTVfsFileRelease(pThis->hVfsBacking);
3836 pThis->hVfsBacking = NIL_RTVFSFILE;
3837
3838 return rc;
3839}
3840
3841
3842/**
3843 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
3844 */
3845static DECLCALLBACK(int) rtFsFatVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3846{
3847 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
3848 return VERR_WRONG_TYPE;
3849}
3850
3851
3852/**
3853 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
3854 */
3855static DECLCALLBACK(int) rtFsFatVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
3856{
3857 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
3858 uint32_t cRefs = RTVfsDirRetain(pThis->hVfsRootDir);
3859 if (cRefs != UINT32_MAX)
3860 {
3861 *phVfsDir = pThis->hVfsRootDir;
3862 return VINF_SUCCESS;
3863 }
3864 return VERR_INTERNAL_ERROR_5;
3865}
3866
3867
3868/**
3869 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
3870 */
3871static DECLCALLBACK(int) rtFsFatVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
3872{
3873 RT_NOREF(pvThis, off, cb, pfUsed);
3874 return VERR_NOT_IMPLEMENTED;
3875}
3876
3877
3878DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsFatVolOps =
3879{
3880 { /* Obj */
3881 RTVFSOBJOPS_VERSION,
3882 RTVFSOBJTYPE_VFS,
3883 "FatVol",
3884 rtFsFatVol_Close,
3885 rtFsFatVol_QueryInfo,
3886 RTVFSOBJOPS_VERSION
3887 },
3888 RTVFSOPS_VERSION,
3889 0 /* fFeatures */,
3890 rtFsFatVol_OpenRoot,
3891 rtFsFatVol_IsRangeInUse,
3892 RTVFSOPS_VERSION
3893};
3894
3895
3896/**
3897 * Tries to detect a DOS 1.x formatted image and fills in the BPB fields.
3898 *
3899 * There is no BPB here, but fortunately, there isn't much variety.
3900 *
3901 * @returns IPRT status code.
3902 * @param pThis The FAT volume instance, BPB derived fields are filled
3903 * in on success.
3904 * @param pBootSector The boot sector.
3905 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
3906 * the boot sector.
3907 * @param pErrInfo Where to return additional error information.
3908 */
3909static int rtFsFatVolTryInitDos1x(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t const *pbFatSector,
3910 PRTERRINFO pErrInfo)
3911{
3912 /*
3913 * PC-DOS 1.0 does a 2fh byte short jump w/o any NOP following it.
3914 * Instead the following are three words and a 9 byte build date
3915 * string. The remaining space is zero filled.
3916 *
3917 * Note! No idea how this would look like for 8" floppies, only got 5"1/4'.
3918 *
3919 * ASSUME all non-BPB disks are using this format.
3920 */
3921 if ( pBootSector->abJmp[0] != 0xeb /* jmp rel8 */
3922 || pBootSector->abJmp[1] < 0x2f
3923 || pBootSector->abJmp[1] >= 0x80
3924 || pBootSector->abJmp[2] == 0x90 /* nop */)
3925 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3926 "No DOS v1.0 bootsector either - invalid jmp: %.3Rhxs", pBootSector->abJmp);
3927 uint32_t const offJump = 2 + pBootSector->abJmp[1];
3928 uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
3929 Assert(offFirstZero >= RT_UOFFSETOF(FATBOOTSECTOR, Bpb));
3930 uint32_t const cbZeroPad = RT_MIN(offJump - offFirstZero,
3931 sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_OFFSETOF(FATBOOTSECTOR, Bpb)));
3932
3933 if (!ASMMemIsAllU8((uint8_t const *)pBootSector + offFirstZero, cbZeroPad, 0))
3934 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3935 "No DOS v1.0 bootsector either - expected zero padding %#x LB %#x: %.*Rhxs",
3936 offFirstZero, cbZeroPad, cbZeroPad, (uint8_t const *)pBootSector + offFirstZero);
3937
3938 /*
3939 * Check the FAT ID so we can tell if this is double or single sided,
3940 * as well as being a valid FAT12 start.
3941 */
3942 if ( (pbFatSector[0] != 0xfe && pbFatSector[0] != 0xff)
3943 || pbFatSector[1] != 0xff
3944 || pbFatSector[2] != 0xff)
3945 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3946 "No DOS v1.0 bootsector either - unexpected start of FAT: %.3Rhxs", pbFatSector);
3947
3948 /*
3949 * Fixed DOS 1.0 config.
3950 */
3951 pThis->enmFatType = RTFSFATTYPE_FAT12;
3952 pThis->enmBpbVersion = RTFSFATBPBVER_NO_BPB;
3953 pThis->bMedia = pbFatSector[0];
3954 pThis->cReservedSectors = 1;
3955 pThis->cbSector = 512;
3956 pThis->cbCluster = pThis->bMedia == 0xfe ? 1024 : 512;
3957 pThis->cFats = 2;
3958 pThis->cbFat = 512;
3959 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * 512;
3960 pThis->aoffFats[1] = pThis->aoffFats[0] + pThis->cbFat;
3961 pThis->offRootDir = pThis->aoffFats[1] + pThis->cbFat;
3962 pThis->cRootDirEntries = 512;
3963 pThis->offFirstCluster = pThis->offRootDir + RT_ALIGN_32(pThis->cRootDirEntries * sizeof(FATDIRENTRY),
3964 pThis->cbSector);
3965 pThis->cbTotalSize = pThis->bMedia == 0xfe ? 8 * 1 * 40 * 512 : 8 * 2 * 40 * 512;
3966 pThis->cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
3967 return VINF_SUCCESS;
3968}
3969
3970
3971/**
3972 * Worker for rtFsFatVolTryInitDos2Plus that handles remaining BPB fields.
3973 *
3974 * @returns IPRT status code.
3975 * @param pThis The FAT volume instance, BPB derived fields are filled
3976 * in on success.
3977 * @param pBootSector The boot sector.
3978 * @param fMaybe331 Set if it could be a DOS v3.31 BPB.
3979 * @param pErrInfo Where to return additional error information.
3980 */
3981static int rtFsFatVolTryInitDos2PlusBpb(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, bool fMaybe331, PRTERRINFO pErrInfo)
3982{
3983 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_2_0;
3984
3985 /*
3986 * Figure total sector count. Could both be zero, in which case we have to
3987 * fall back on the size of the backing stuff.
3988 */
3989 if (pBootSector->Bpb.Bpb20.cTotalSectors16 != 0)
3990 pThis->cbTotalSize = pBootSector->Bpb.Bpb20.cTotalSectors16 * pThis->cbSector;
3991 else if ( pBootSector->Bpb.Bpb331.cTotalSectors32 != 0
3992 && fMaybe331)
3993 {
3994 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_3_31;
3995 pThis->cbTotalSize = pBootSector->Bpb.Bpb331.cTotalSectors32 * (uint64_t)pThis->cbSector;
3996 }
3997 else
3998 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
3999 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
4000 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4001 "Bogus FAT12/16 total or reserved sector count: %#x vs %#x",
4002 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
4003
4004 /*
4005 * The fat size. Complete FAT offsets.
4006 */
4007 if ( pBootSector->Bpb.Bpb20.cSectorsPerFat == 0
4008 || ((uint32_t)pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cFats + 1) * pThis->cbSector > pThis->cbTotalSize)
4009 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 sectors per FAT: %#x (total sectors %#RX64)",
4010 pBootSector->Bpb.Bpb20.cSectorsPerFat, pThis->cbTotalSize / pThis->cbSector);
4011 pThis->cbFat = pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cbSector;
4012
4013 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
4014 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
4015 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
4016
4017 /*
4018 * Do root directory calculations.
4019 */
4020 pThis->idxRootDirCluster = UINT32_MAX;
4021 pThis->offRootDir = pThis->aoffFats[pThis->cFats];
4022 if (pThis->cRootDirEntries == 0)
4023 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero FAT12/16 root directory size");
4024 pThis->cbRootDir = pThis->cRootDirEntries * sizeof(FATDIRENTRY);
4025 pThis->cbRootDir = RT_ALIGN_32(pThis->cbRootDir, pThis->cbSector);
4026
4027 /*
4028 * First cluster and cluster count checks and calcs. Determin FAT type.
4029 */
4030 pThis->offFirstCluster = pThis->offRootDir + pThis->cbRootDir;
4031 uint64_t cbSystemStuff = pThis->offFirstCluster - pThis->offBootSector;
4032 if (cbSystemStuff >= pThis->cbTotalSize)
4033 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 total size, root dir, or fat size");
4034 pThis->cClusters = (pThis->cbTotalSize - cbSystemStuff) / pThis->cbCluster;
4035
4036 if (pThis->cClusters >= FAT_MAX_FAT16_DATA_CLUSTERS)
4037 {
4038 pThis->cClusters = FAT_MAX_FAT16_DATA_CLUSTERS;
4039 pThis->enmFatType = RTFSFATTYPE_FAT16;
4040 }
4041 else if (pThis->cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS)
4042 pThis->enmFatType = RTFSFATTYPE_FAT16;
4043 else
4044 pThis->enmFatType = RTFSFATTYPE_FAT12; /** @todo Not sure if this is entirely the right way to go about it... */
4045
4046 uint32_t cClustersPerFat;
4047 if (pThis->enmFatType == RTFSFATTYPE_FAT16)
4048 cClustersPerFat = pThis->cbFat / 2;
4049 else
4050 cClustersPerFat = pThis->cbFat * 2 / 3;
4051 if (pThis->cClusters > cClustersPerFat)
4052 pThis->cClusters = cClustersPerFat;
4053
4054 return VINF_SUCCESS;
4055}
4056
4057
4058/**
4059 * Worker for rtFsFatVolTryInitDos2Plus and rtFsFatVolTryInitDos2PlusFat32 that
4060 * handles common extended BPBs fields.
4061 *
4062 * @returns IPRT status code.
4063 * @param pThis The FAT volume instance.
4064 * @param bExtSignature The extended BPB signature.
4065 * @param uSerialNumber The serial number.
4066 * @param pachLabel Pointer to the volume label field.
4067 * @param pachType Pointer to the file system type field.
4068 */
4069static void rtFsFatVolInitCommonEbpbBits(PRTFSFATVOL pThis, uint8_t bExtSignature, uint32_t uSerialNumber,
4070 char const *pachLabel, char const *pachType)
4071{
4072 pThis->uSerialNo = uSerialNumber;
4073 if (bExtSignature == FATEBPB_SIGNATURE)
4074 {
4075 memcpy(pThis->szLabel, pachLabel, RT_SIZEOFMEMB(FATEBPB, achLabel));
4076 pThis->szLabel[RT_SIZEOFMEMB(FATEBPB, achLabel)] = '\0';
4077 RTStrStrip(pThis->szLabel);
4078
4079 memcpy(pThis->szType, pachType, RT_SIZEOFMEMB(FATEBPB, achType));
4080 pThis->szType[RT_SIZEOFMEMB(FATEBPB, achType)] = '\0';
4081 RTStrStrip(pThis->szType);
4082 }
4083 else
4084 {
4085 pThis->szLabel[0] = '\0';
4086 pThis->szType[0] = '\0';
4087 }
4088}
4089
4090
4091/**
4092 * Worker for rtFsFatVolTryInitDos2Plus that deals with FAT32.
4093 *
4094 * @returns IPRT status code.
4095 * @param pThis The FAT volume instance, BPB derived fields are filled
4096 * in on success.
4097 * @param pBootSector The boot sector.
4098 * @param pErrInfo Where to return additional error information.
4099 */
4100static int rtFsFatVolTryInitDos2PlusFat32(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, PRTERRINFO pErrInfo)
4101{
4102 pThis->enmFatType = RTFSFATTYPE_FAT32;
4103 pThis->enmBpbVersion = pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE
4104 ? RTFSFATBPBVER_FAT32_29 : RTFSFATBPBVER_FAT32_28;
4105 pThis->fFat32Flags = pBootSector->Bpb.Fat32Ebpb.fFlags;
4106
4107 if (pBootSector->Bpb.Fat32Ebpb.uVersion != FAT32EBPB_VERSION_0_0)
4108 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Unsupported FAT32 version: %d.%d (%#x)",
4109 RT_HI_U8(pBootSector->Bpb.Fat32Ebpb.uVersion), RT_LO_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),
4110 pBootSector->Bpb.Fat32Ebpb.uVersion);
4111
4112 /*
4113 * Figure total sector count. We expected it to be filled in.
4114 */
4115 bool fUsing64BitTotalSectorCount = false;
4116 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 != 0)
4117 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 * pThis->cbSector;
4118 else if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 != 0)
4119 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 * (uint64_t)pThis->cbSector;
4120 else if ( pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 <= UINT64_MAX / 512
4121 && pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 > 3
4122 && pBootSector->Bpb.Fat32Ebpb.bExtSignature != FATEBPB_SIGNATURE_OLD)
4123 {
4124 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 * pThis->cbSector;
4125 fUsing64BitTotalSectorCount = true;
4126 }
4127 else
4128 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 total sector count out of range: %#RX64",
4129 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64);
4130 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
4131 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4132 "Bogus FAT32 total or reserved sector count: %#x vs %#x",
4133 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
4134
4135 /*
4136 * Fat size. We check the 16-bit field even if it probably should be zero all the time.
4137 */
4138 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat != 0)
4139 {
4140 if ( pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != 0
4141 && pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat)
4142 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4143 "Both 16-bit and 32-bit FAT size fields are set: %#RX16 vs %#RX32",
4144 pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat, pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
4145 pThis->cbFat = pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat * pThis->cbSector;
4146 }
4147 else
4148 {
4149 uint64_t cbFat = pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 * (uint64_t)pThis->cbSector;
4150 if ( cbFat == 0
4151 || cbFat >= FAT_MAX_FAT32_TOTAL_CLUSTERS * 4 + pThis->cbSector * 16)
4152 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4153 "Bogus 32-bit FAT size: %#RX32", pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
4154 pThis->cbFat = (uint32_t)cbFat;
4155 }
4156
4157 /*
4158 * Complete the FAT offsets and first cluster offset, then calculate number
4159 * of data clusters.
4160 */
4161 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
4162 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
4163 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
4164 pThis->offFirstCluster = pThis->aoffFats[pThis->cFats];
4165
4166 if (pThis->offFirstCluster - pThis->offBootSector >= pThis->cbTotalSize)
4167 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4168 "Bogus 32-bit FAT size or total sector count: cFats=%d cbFat=%#x cbTotalSize=%#x",
4169 pThis->cFats, pThis->cbFat, pThis->cbTotalSize);
4170
4171 uint64_t cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
4172 if (cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS)
4173 pThis->cClusters = (uint32_t)cClusters;
4174 else
4175 pThis->cClusters = FAT_MAX_FAT32_DATA_CLUSTERS;
4176 if (pThis->cClusters > (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER))
4177 pThis->cClusters = (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER);
4178
4179 /*
4180 * Root dir cluster.
4181 */
4182 if ( pBootSector->Bpb.Fat32Ebpb.uRootDirCluster < FAT_FIRST_DATA_CLUSTER
4183 || pBootSector->Bpb.Fat32Ebpb.uRootDirCluster >= pThis->cClusters)
4184 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4185 "Bogus FAT32 root directory cluster: %#x", pBootSector->Bpb.Fat32Ebpb.uRootDirCluster);
4186 pThis->idxRootDirCluster = pBootSector->Bpb.Fat32Ebpb.uRootDirCluster;
4187 pThis->offRootDir = pThis->offFirstCluster
4188 + (pBootSector->Bpb.Fat32Ebpb.uRootDirCluster - FAT_FIRST_DATA_CLUSTER) * pThis->cbCluster;
4189
4190 /*
4191 * Info sector.
4192 */
4193 if ( pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == 0
4194 || pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == UINT16_MAX)
4195 pThis->offFat32InfoSector = UINT64_MAX;
4196 else if (pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo >= pThis->cReservedSectors)
4197 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4198 "Bogus FAT32 info sector number: %#x (reserved sectors %#x)",
4199 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pThis->cReservedSectors);
4200 else
4201 {
4202 pThis->offFat32InfoSector = pThis->cbSector * pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo + pThis->offBootSector;
4203 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->offFat32InfoSector,
4204 &pThis->Fat32InfoSector, sizeof(pThis->Fat32InfoSector), NULL);
4205 if (RT_FAILURE(rc))
4206 return RTErrInfoSetF(pErrInfo, rc, "Failed to read FAT32 info sector at offset %#RX64", pThis->offFat32InfoSector);
4207 if ( pThis->Fat32InfoSector.uSignature1 != FAT32INFOSECTOR_SIGNATURE_1
4208 || pThis->Fat32InfoSector.uSignature2 != FAT32INFOSECTOR_SIGNATURE_2
4209 || pThis->Fat32InfoSector.uSignature3 != FAT32INFOSECTOR_SIGNATURE_3)
4210 return RTErrInfoSetF(pErrInfo, rc, "FAT32 info sector signature mismatch: %#x %#x %#x",
4211 pThis->Fat32InfoSector.uSignature1, pThis->Fat32InfoSector.uSignature2,
4212 pThis->Fat32InfoSector.uSignature3);
4213 }
4214
4215 /*
4216 * Boot sector copy.
4217 */
4218 if ( pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == 0
4219 || pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == UINT16_MAX)
4220 {
4221 pThis->cBootSectorCopies = 0;
4222 pThis->offBootSectorCopies = UINT64_MAX;
4223 }
4224 else if (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo >= pThis->cReservedSectors)
4225 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4226 "Bogus FAT32 info boot sector copy location: %#x (reserved sectors %#x)",
4227 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo, pThis->cReservedSectors);
4228 else
4229 {
4230 /** @todo not sure if cbSector is correct here. */
4231 pThis->cBootSectorCopies = 3;
4232 if ( (uint32_t)pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo + pThis->cBootSectorCopies
4233 > pThis->cReservedSectors)
4234 pThis->cBootSectorCopies = (uint8_t)(pThis->cReservedSectors - pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
4235 pThis->offBootSectorCopies = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo * pThis->cbSector + pThis->offBootSector;
4236 if ( pThis->offFat32InfoSector != UINT64_MAX
4237 && pThis->offFat32InfoSector - pThis->offBootSectorCopies < (uint64_t)(pThis->cBootSectorCopies * pThis->cbSector))
4238 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 info sector and boot sector copies overlap: %#x vs %#x",
4239 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
4240 }
4241
4242 /*
4243 * Serial number, label and type.
4244 */
4245 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Fat32Ebpb.bExtSignature, pBootSector->Bpb.Fat32Ebpb.uSerialNumber,
4246 pBootSector->Bpb.Fat32Ebpb.achLabel,
4247 fUsing64BitTotalSectorCount ? pBootSector->achOemName : pBootSector->Bpb.Fat32Ebpb.achLabel);
4248 if (pThis->szType[0] == '\0')
4249 memcpy(pThis->szType, "FAT32", 6);
4250
4251 return VINF_SUCCESS;
4252}
4253
4254
4255/**
4256 * Tries to detect a DOS 2.0+ formatted image and fills in the BPB fields.
4257 *
4258 * We ASSUME BPB here, but need to figure out which version of the BPB it is,
4259 * which is lots of fun.
4260 *
4261 * @returns IPRT status code.
4262 * @param pThis The FAT volume instance, BPB derived fields are filled
4263 * in on success.
4264 * @param pBootSector The boot sector.
4265 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
4266 * the boot sector. On successful return it will contain
4267 * the first FAT sector.
4268 * @param pErrInfo Where to return additional error information.
4269 */
4270static int rtFsFatVolTryInitDos2Plus(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t *pbFatSector, PRTERRINFO pErrInfo)
4271{
4272 /*
4273 * Check if we've got a known jump instruction first, because that will
4274 * give us a max (E)BPB size hint.
4275 */
4276 uint8_t offJmp = UINT8_MAX;
4277 if ( pBootSector->abJmp[0] == 0xeb
4278 && pBootSector->abJmp[1] <= 0x7f)
4279 offJmp = pBootSector->abJmp[1] + 2;
4280 else if ( pBootSector->abJmp[0] == 0x90
4281 && pBootSector->abJmp[1] == 0xeb
4282 && pBootSector->abJmp[2] <= 0x7f)
4283 offJmp = pBootSector->abJmp[2] + 3;
4284 else if ( pBootSector->abJmp[0] == 0xe9
4285 && pBootSector->abJmp[2] <= 0x7f)
4286 offJmp = RT_MIN(127, RT_MAKE_U16(pBootSector->abJmp[1], pBootSector->abJmp[2]));
4287 uint8_t const cbMaxBpb = offJmp - RT_OFFSETOF(FATBOOTSECTOR, Bpb);
4288
4289 /*
4290 * Do the basic DOS v2.0 BPB fields.
4291 */
4292 if (cbMaxBpb < sizeof(FATBPB20))
4293 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4294 "DOS signature, but jmp too short for any BPB: %#x (max %#x BPB)", offJmp, cbMaxBpb);
4295
4296 if (pBootSector->Bpb.Bpb20.cFats == 0)
4297 return RTErrInfoSet(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, number of FATs is zero, so not FAT file system");
4298 if (pBootSector->Bpb.Bpb20.cFats > 4)
4299 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many FATs: %#x", pBootSector->Bpb.Bpb20.cFats);
4300 pThis->cFats = pBootSector->Bpb.Bpb20.cFats;
4301
4302 if (!FATBPB_MEDIA_IS_VALID(pBootSector->Bpb.Bpb20.bMedia))
4303 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4304 "DOS signature, invalid media byte: %#x", pBootSector->Bpb.Bpb20.bMedia);
4305 pThis->bMedia = pBootSector->Bpb.Bpb20.bMedia;
4306
4307 if (!RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cbSector))
4308 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4309 "DOS signature, sector size not power of two: %#x", pBootSector->Bpb.Bpb20.cbSector);
4310 if ( pBootSector->Bpb.Bpb20.cbSector != 512
4311 && pBootSector->Bpb.Bpb20.cbSector != 4096
4312 && pBootSector->Bpb.Bpb20.cbSector != 1024
4313 && pBootSector->Bpb.Bpb20.cbSector != 128)
4314 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4315 "DOS signature, unsupported sector size: %#x", pBootSector->Bpb.Bpb20.cbSector);
4316 pThis->cbSector = pBootSector->Bpb.Bpb20.cbSector;
4317
4318 if ( !RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cSectorsPerCluster)
4319 || !pBootSector->Bpb.Bpb20.cSectorsPerCluster)
4320 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, cluster size not non-zero power of two: %#x",
4321 pBootSector->Bpb.Bpb20.cSectorsPerCluster);
4322 pThis->cbCluster = pBootSector->Bpb.Bpb20.cSectorsPerCluster * pThis->cbSector;
4323
4324 uint64_t const cMaxRoot = (pThis->cbBacking - pThis->offBootSector - 512) / sizeof(FATDIRENTRY); /* we'll check again later. */
4325 if (pBootSector->Bpb.Bpb20.cMaxRootDirEntries >= cMaxRoot)
4326 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many root entries: %#x (max %#RX64)",
4327 pBootSector->Bpb.Bpb20.cSectorsPerCluster, cMaxRoot);
4328 pThis->cRootDirEntries = pBootSector->Bpb.Bpb20.cMaxRootDirEntries;
4329
4330 if ( pBootSector->Bpb.Bpb20.cReservedSectors == 0
4331 || pBootSector->Bpb.Bpb20.cReservedSectors >= _32K)
4332 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4333 "DOS signature, bogus reserved sector count: %#x", pBootSector->Bpb.Bpb20.cReservedSectors);
4334 pThis->cReservedSectors = pBootSector->Bpb.Bpb20.cReservedSectors;
4335 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * pThis->cbSector;
4336
4337 /*
4338 * Jump ahead and check for FAT32 EBPB.
4339 * If found, we simply ASSUME it's a FAT32 file system.
4340 */
4341 int rc;
4342 if ( ( sizeof(FAT32EBPB) <= cbMaxBpb
4343 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE)
4344 || ( RT_OFFSETOF(FAT32EBPB, achLabel) <= cbMaxBpb
4345 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
4346 {
4347 rc = rtFsFatVolTryInitDos2PlusFat32(pThis, pBootSector, pErrInfo);
4348 if (RT_FAILURE(rc))
4349 return rc;
4350 }
4351 else
4352 {
4353 /*
4354 * Check for extended BPB, otherwise we'll have to make qualified guesses
4355 * about what kind of BPB we're up against based on jmp offset and zero fields.
4356 * ASSUMES either FAT16 or FAT12.
4357 */
4358 if ( ( sizeof(FATEBPB) <= cbMaxBpb
4359 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE)
4360 || ( RT_OFFSETOF(FATEBPB, achLabel) <= cbMaxBpb
4361 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
4362 {
4363 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Ebpb.bExtSignature, pBootSector->Bpb.Ebpb.uSerialNumber,
4364 pBootSector->Bpb.Ebpb.achLabel, pBootSector->Bpb.Ebpb.achType);
4365 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, true /*fMaybe331*/, pErrInfo);
4366 pThis->enmBpbVersion = pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE
4367 ? RTFSFATBPBVER_EXT_29 : RTFSFATBPBVER_EXT_28;
4368 }
4369 else
4370 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, cbMaxBpb >= sizeof(FATBPB331), pErrInfo);
4371 if (RT_FAILURE(rc))
4372 return rc;
4373 if (pThis->szType[0] == '\0')
4374 memcpy(pThis->szType, pThis->enmFatType == RTFSFATTYPE_FAT12 ? "FAT12" : "FAT16", 6);
4375 }
4376
4377 /*
4378 * Check the FAT ID. May have to read a bit of the FAT into the buffer.
4379 */
4380 if (pThis->aoffFats[0] != pThis->offBootSector + 512)
4381 {
4382 rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0], pbFatSector, 512, NULL);
4383 if (RT_FAILURE(rc))
4384 return RTErrInfoSet(pErrInfo, rc, "error reading first FAT sector");
4385 }
4386 if (pbFatSector[0] != pThis->bMedia)
4387 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4388 "Media byte and FAT ID mismatch: %#x vs %#x (%.7Rhxs)", pbFatSector[0], pThis->bMedia, pbFatSector);
4389 switch (pThis->enmFatType)
4390 {
4391 case RTFSFATTYPE_FAT12:
4392 if ((pbFatSector[1] & 0xf) != 0xf)
4393 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT12): %.3Rhxs", pbFatSector);
4394 pThis->idxMaxLastCluster = FAT_LAST_FAT12_DATA_CLUSTER;
4395 pThis->idxEndOfChain = (pbFatSector[1] >> 4) | ((uint32_t)pbFatSector[2] << 4);
4396 break;
4397
4398 case RTFSFATTYPE_FAT16:
4399 if (pbFatSector[1] != 0xff)
4400 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT16): %.4Rhxs", pbFatSector);
4401 pThis->idxMaxLastCluster = FAT_LAST_FAT16_DATA_CLUSTER;
4402 pThis->idxEndOfChain = RT_MAKE_U16(pbFatSector[2], pbFatSector[3]);
4403 break;
4404
4405 case RTFSFATTYPE_FAT32:
4406 if ( pbFatSector[1] != 0xff
4407 || pbFatSector[2] != 0xff
4408 || (pbFatSector[3] & 0x0f) != 0x0f)
4409 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT32): %.8Rhxs", pbFatSector);
4410 pThis->idxMaxLastCluster = FAT_LAST_FAT32_DATA_CLUSTER;
4411 pThis->idxEndOfChain = RT_MAKE_U32_FROM_U8(pbFatSector[4], pbFatSector[5], pbFatSector[6], pbFatSector[7]);
4412 break;
4413
4414 default: AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4415 }
4416 if (pThis->idxEndOfChain <= pThis->idxMaxLastCluster)
4417 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus formatter end-of-chain value: %#x, must be above %#x",
4418 pThis->idxEndOfChain, pThis->idxMaxLastCluster);
4419
4420 RT_NOREF(pbFatSector);
4421 return VINF_SUCCESS;
4422}
4423
4424
4425/**
4426 * Given a power of two value @a cb return exponent value.
4427 *
4428 * @returns Shift count
4429 * @param cb The value.
4430 */
4431static uint8_t rtFsFatVolCalcByteShiftCount(uint32_t cb)
4432{
4433 Assert(RT_IS_POWER_OF_TWO(cb));
4434 unsigned iBit = ASMBitFirstSetU32(cb);
4435 Assert(iBit >= 1);
4436 iBit--;
4437 return iBit;
4438}
4439
4440
4441/**
4442 * Worker for RTFsFatVolOpen.
4443 *
4444 * @returns IPRT status code.
4445 * @param pThis The FAT VFS instance to initialize.
4446 * @param hVfsSelf The FAT VFS handle (no reference consumed).
4447 * @param hVfsBacking The file backing the alleged FAT file system.
4448 * Reference is consumed (via rtFsFatVol_Destroy).
4449 * @param fReadOnly Readonly or readwrite mount.
4450 * @param offBootSector The boot sector offset in bytes.
4451 * @param pErrInfo Where to return additional error info. Can be NULL.
4452 */
4453static int rtFsFatVolTryInit(PRTFSFATVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking,
4454 bool fReadOnly, uint64_t offBootSector, PRTERRINFO pErrInfo)
4455{
4456 /*
4457 * First initialize the state so that rtFsFatVol_Destroy won't trip up.
4458 */
4459 pThis->hVfsSelf = hVfsSelf;
4460 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsFatVol_Destroy releases it. */
4461 pThis->cbBacking = 0;
4462 pThis->offBootSector = offBootSector;
4463 pThis->offNanoUTC = RTTimeLocalDeltaNano();
4464 pThis->offMinUTC = pThis->offNanoUTC / RT_NS_1MIN;
4465 pThis->fReadOnly = fReadOnly;
4466 pThis->cReservedSectors = 1;
4467
4468 pThis->cbSector = 512;
4469 pThis->cbCluster = 512;
4470 pThis->cClusters = 0;
4471 pThis->offFirstCluster = 0;
4472 pThis->cbTotalSize = 0;
4473
4474 pThis->enmFatType = RTFSFATTYPE_INVALID;
4475 pThis->cFatEntries = 0;
4476 pThis->cFats = 0;
4477 pThis->cbFat = 0;
4478 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aoffFats); i++)
4479 pThis->aoffFats[i] = UINT64_MAX;
4480 pThis->pFatCache = NULL;
4481
4482 pThis->offRootDir = UINT64_MAX;
4483 pThis->idxRootDirCluster = UINT32_MAX;
4484 pThis->cRootDirEntries = UINT32_MAX;
4485 pThis->cbRootDir = 0;
4486 pThis->hVfsRootDir = NIL_RTVFSDIR;
4487 pThis->pRootDir = NULL;
4488
4489 pThis->uSerialNo = 0;
4490 pThis->szLabel[0] = '\0';
4491 pThis->szType[0] = '\0';
4492 pThis->cBootSectorCopies = 0;
4493 pThis->fFat32Flags = 0;
4494 pThis->offBootSectorCopies = UINT64_MAX;
4495 pThis->offFat32InfoSector = UINT64_MAX;
4496 RT_ZERO(pThis->Fat32InfoSector);
4497
4498 /*
4499 * Get stuff that may fail.
4500 */
4501 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
4502 if (RT_FAILURE(rc))
4503 return rc;
4504 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
4505
4506 /*
4507 * Read the boot sector and the following sector (start of the allocation
4508 * table unless it a FAT32 FS). We'll then validate the boot sector and
4509 * start of the FAT, expanding the BPB into the instance data.
4510 */
4511 union
4512 {
4513 uint8_t ab[512*2];
4514 uint16_t au16[512*2 / 2];
4515 uint32_t au32[512*2 / 4];
4516 FATBOOTSECTOR BootSector;
4517 FAT32INFOSECTOR InfoSector;
4518 } Buf;
4519 RT_ZERO(Buf);
4520
4521 rc = RTVfsFileReadAt(hVfsBacking, offBootSector, &Buf.BootSector, 512 * 2, NULL);
4522 if (RT_FAILURE(rc))
4523 return RTErrInfoSet(pErrInfo, rc, "Unable to read bootsect");
4524
4525 /*
4526 * Extract info from the BPB and validate the two special FAT entries.
4527 *
4528 * Check the DOS signature first. The PC-DOS 1.0 boot floppy does not have
4529 * a signature and we ASSUME this is the case for all flopies formated by it.
4530 */
4531 if (Buf.BootSector.uSignature != FATBOOTSECTOR_SIGNATURE)
4532 {
4533 if (Buf.BootSector.uSignature != 0)
4534 return RTErrInfoSetF(pErrInfo, rc, "No DOS bootsector signature: %#06x", Buf.BootSector.uSignature);
4535 rc = rtFsFatVolTryInitDos1x(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
4536 }
4537 else
4538 rc = rtFsFatVolTryInitDos2Plus(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
4539 if (RT_FAILURE(rc))
4540 return rc;
4541
4542 /*
4543 * Calc shift counts.
4544 */
4545 pThis->cSectorByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbSector);
4546 pThis->cClusterByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbCluster);
4547
4548 /*
4549 * Setup the FAT cache.
4550 */
4551 rc = rtFsFatClusterMap_Create(pThis, &Buf.ab[512], pErrInfo);
4552 if (RT_FAILURE(rc))
4553 return rc;
4554
4555 /*
4556 * Create the root directory fun.
4557 */
4558 if (pThis->idxRootDirCluster == UINT32_MAX)
4559 rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
4560 UINT32_MAX, pThis->offRootDir, pThis->cbRootDir,
4561 &pThis->hVfsRootDir, &pThis->pRootDir);
4562 else
4563 rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
4564 pThis->idxRootDirCluster, UINT64_MAX, pThis->cbRootDir,
4565 &pThis->hVfsRootDir, &pThis->pRootDir);
4566 return rc;
4567}
4568
4569
4570/**
4571 * Opens a FAT file system volume.
4572 *
4573 * @returns IPRT status code.
4574 * @param hVfsFileIn The file or device backing the volume.
4575 * @param fReadOnly Whether to mount it read-only.
4576 * @param offBootSector The offset of the boot sector relative to the start
4577 * of @a hVfsFileIn. Pass 0 for floppies.
4578 * @param phVfs Where to return the virtual file system handle.
4579 * @param pErrInfo Where to return additional error information.
4580 */
4581RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, uint64_t offBootSector, PRTVFS phVfs, PRTERRINFO pErrInfo)
4582{
4583 /*
4584 * Quick input validation.
4585 */
4586 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
4587 *phVfs = NIL_RTVFS;
4588
4589 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
4590 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
4591
4592 /*
4593 * Create a new FAT VFS instance and try initialize it using the given input file.
4594 */
4595 RTVFS hVfs = NIL_RTVFS;
4596 void *pvThis = NULL;
4597 int rc = RTVfsNew(&g_rtFsFatVolOps, sizeof(RTFSFATVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
4598 if (RT_SUCCESS(rc))
4599 {
4600 rc = rtFsFatVolTryInit((PRTFSFATVOL)pvThis, hVfs, hVfsFileIn, fReadOnly, offBootSector, pErrInfo);
4601 if (RT_SUCCESS(rc))
4602 *phVfs = hVfs;
4603 else
4604 RTVfsRelease(hVfs);
4605 }
4606 else
4607 RTVfsFileRelease(hVfsFileIn);
4608 return rc;
4609}
4610
4611
4612
4613
4614/**
4615 * Fills a range in the file with zeros in the most efficient manner.
4616 *
4617 * @returns IPRT status code.
4618 * @param hVfsFile The file to write to.
4619 * @param off Where to start filling with zeros.
4620 * @param cbZeros How many zero blocks to write.
4621 */
4622static int rtFsFatVolWriteZeros(RTVFSFILE hVfsFile, uint64_t off, uint32_t cbZeros)
4623{
4624 while (cbZeros > 0)
4625 {
4626 uint32_t cbToWrite = sizeof(g_abRTZero64K);
4627 if (cbToWrite > cbZeros)
4628 cbToWrite = cbZeros;
4629 int rc = RTVfsFileWriteAt(hVfsFile, off, g_abRTZero64K, cbToWrite, NULL);
4630 if (RT_FAILURE(rc))
4631 return rc;
4632 off += cbToWrite;
4633 cbZeros -= cbToWrite;
4634 }
4635 return VINF_SUCCESS;
4636}
4637
4638
4639/**
4640 * Formats a FAT volume.
4641 *
4642 * @returns IRPT status code.
4643 * @param hVfsFile The volume file.
4644 * @param offVol The offset into @a hVfsFile of the file.
4645 * Typically 0.
4646 * @param cbVol The size of the volume. Pass 0 if the rest of
4647 * hVfsFile should be used.
4648 * @param fFlags See RTFSFATVOL_FMT_F_XXX.
4649 * @param cbSector The logical sector size. Must be power of two.
4650 * Optional, pass zero to use 512.
4651 * @param cSectorsPerCluster Number of sectors per cluster. Power of two.
4652 * Optional, pass zero to auto detect.
4653 * @param enmFatType The FAT type (12, 16, 32) to use.
4654 * Optional, pass RTFSFATTYPE_INVALID for default.
4655 * @param cHeads The number of heads to report in the BPB.
4656 * Optional, pass zero to auto detect.
4657 * @param cSectorsPerTrack The number of sectors per track to put in the
4658 * BPB. Optional, pass zero to auto detect.
4659 * @param bMedia The media byte value and FAT ID to use.
4660 * Optional, pass zero to auto detect.
4661 * @param cRootDirEntries Number of root directory entries.
4662 * Optional, pass zero to auto detect.
4663 * @param cHiddenSectors Number of hidden sectors. Pass 0 for
4664 * unpartitioned media.
4665 * @param pErrInfo Additional error information, maybe. Optional.
4666 */
4667RTDECL(int) RTFsFatVolFormat(RTVFSFILE hVfsFile, uint64_t offVol, uint64_t cbVol, uint32_t fFlags, uint16_t cbSector,
4668 uint16_t cSectorsPerCluster, RTFSFATTYPE enmFatType, uint32_t cHeads, uint32_t cSectorsPerTrack,
4669 uint8_t bMedia, uint16_t cRootDirEntries, uint32_t cHiddenSectors, PRTERRINFO pErrInfo)
4670{
4671 int rc;
4672 uint32_t cFats = 2;
4673
4674 /*
4675 * Validate input.
4676 */
4677 if (!cbSector)
4678 cbSector = 512;
4679 else
4680 AssertMsgReturn( cbSector == 128
4681 || cbSector == 512
4682 || cbSector == 1024
4683 || cbSector == 4096,
4684 ("cbSector=%#x\n", cbSector),
4685 VERR_INVALID_PARAMETER);
4686 AssertMsgReturn(cSectorsPerCluster == 0 || (cSectorsPerCluster <= 128 && RT_IS_POWER_OF_TWO(cSectorsPerCluster)),
4687 ("cSectorsPerCluster=%#x\n", cSectorsPerCluster), VERR_INVALID_PARAMETER);
4688 if (bMedia != 0)
4689 {
4690 AssertMsgReturn(FAT_ID_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
4691 AssertMsgReturn(FATBPB_MEDIA_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
4692 }
4693 AssertReturn(!(fFlags & ~RTFSFATVOL_FMT_F_VALID_MASK), VERR_INVALID_FLAGS);
4694 AssertReturn(enmFatType >= RTFSFATTYPE_INVALID && enmFatType < RTFSFATTYPE_END, VERR_INVALID_PARAMETER);
4695
4696 if (!cbVol)
4697 {
4698 uint64_t cbFile;
4699 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
4700 AssertRCReturn(rc, rc);
4701 AssertMsgReturn(cbFile > offVol, ("cbFile=%#RX64 offVol=%#RX64\n", cbFile, offVol), VERR_INVALID_PARAMETER);
4702 cbVol = cbFile - offVol;
4703 }
4704 uint64_t const cSectorsInVol = cbVol / cbSector;
4705
4706 /*
4707 * Guess defaults if necessary.
4708 */
4709 if (!cSectorsPerCluster || !cHeads || !cSectorsPerTrack || !bMedia || !cRootDirEntries)
4710 {
4711 static struct
4712 {
4713 uint64_t cbVol;
4714 uint8_t bMedia;
4715 uint8_t cHeads;
4716 uint8_t cSectorsPerTrack;
4717 uint8_t cSectorsPerCluster;
4718 uint16_t cRootDirEntries;
4719 } s_aDefaults[] =
4720 {
4721 /* cbVol, bMedia, cHeads, cSectorsPTrk, cSectorsPClstr, cRootDirEntries */
4722 { 163840, 0xfe, /* cyl: 40,*/ 1, 8, 1, 64 },
4723 { 184320, 0xfc, /* cyl: 40,*/ 1, 9, 2, 64 },
4724 { 327680, 0xff, /* cyl: 40,*/ 2, 8, 2, 112 },
4725 { 368640, 0xfd, /* cyl: 40,*/ 2, 9, 2, 112 },
4726 { 737280, 0xf9, /* cyl: 80,*/ 2, 9, 2, 112 },
4727 { 1228800, 0xf9, /* cyl: 80,*/ 2, 15, 2, 112 },
4728 { 1474560, 0xf0, /* cyl: 80,*/ 2, 18, 1, 224 },
4729 { 2949120, 0xf0, /* cyl: 80,*/ 2, 36, 2, 224 },
4730 { 528482304, 0xf8, /* cyl: 1024,*/ 16, 63, 0, 512 }, // 504MB limit
4731 { UINT64_C(7927234560), 0xf8, /* cyl: 1024,*/ 240, 63, 0, 512 }, // 7.3GB limit
4732 { UINT64_C(8422686720), 0xf8, /* cyl: 1024,*/ 255, 63, 0, 512 }, // 7.84GB limit
4733
4734 };
4735 uint32_t iDefault = 0;
4736 while ( iDefault < RT_ELEMENTS(s_aDefaults) - 1U
4737 && cbVol > s_aDefaults[iDefault].cbVol)
4738 iDefault++;
4739 if (!cHeads)
4740 cHeads = s_aDefaults[iDefault].cHeads;
4741 if (!cSectorsPerTrack)
4742 cSectorsPerTrack = s_aDefaults[iDefault].cSectorsPerTrack;
4743 if (!bMedia)
4744 bMedia = s_aDefaults[iDefault].bMedia;
4745 if (!cRootDirEntries)
4746 cRootDirEntries = s_aDefaults[iDefault].cRootDirEntries;
4747 if (!cSectorsPerCluster)
4748 {
4749 cSectorsPerCluster = s_aDefaults[iDefault].cSectorsPerCluster;
4750 if (!cSectorsPerCluster)
4751 {
4752 uint32_t cbFat12Overhead = cbSector /* boot sector */
4753 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
4754 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4755 uint32_t cbFat16Overhead = cbSector /* boot sector */
4756 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
4757 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4758
4759 if ( enmFatType == RTFSFATTYPE_FAT12
4760 || cbVol <= cbFat12Overhead + FAT_MAX_FAT12_DATA_CLUSTERS * 4 * cbSector)
4761 {
4762 enmFatType = RTFSFATTYPE_FAT12;
4763 cSectorsPerCluster = 1;
4764 while ( cSectorsPerCluster < 128
4765 && cSectorsInVol
4766 > cbFat12Overhead / cbSector
4767 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT12_DATA_CLUSTERS
4768 + cSectorsPerCluster - 1)
4769 cSectorsPerCluster <<= 1;
4770 }
4771 else if ( enmFatType == RTFSFATTYPE_FAT16
4772 || cbVol <= cbFat16Overhead + FAT_MAX_FAT16_DATA_CLUSTERS * 128 * cbSector)
4773 {
4774 enmFatType = RTFSFATTYPE_FAT16;
4775 cSectorsPerCluster = 1;
4776 while ( cSectorsPerCluster < 128
4777 && cSectorsInVol
4778 > cbFat12Overhead / cbSector
4779 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT16_DATA_CLUSTERS
4780 + cSectorsPerCluster - 1)
4781 cSectorsPerCluster <<= 1;
4782 }
4783 else
4784 {
4785 /* The target here is keeping the FAT size below 8MB. Seems windows
4786 likes a minimum 4KB cluster size as wells as a max of 32KB (googling). */
4787 enmFatType = RTFSFATTYPE_FAT32;
4788 uint32_t cbFat32Overhead = cbSector * 32 /* boot sector, info sector, boot sector copies, reserved sectors */
4789 + _8M * cFats;
4790 if (cbSector >= _4K)
4791 cSectorsPerCluster = 1;
4792 else
4793 cSectorsPerCluster = _4K / cbSector;
4794 while ( cSectorsPerCluster < 128
4795 && cSectorsPerCluster * cbSector < _32K
4796 && cSectorsInVol > cbFat32Overhead / cbSector + (uint64_t)cSectorsPerCluster * _2M)
4797 cSectorsPerCluster <<= 1;
4798 }
4799 }
4800 }
4801 }
4802 Assert(cSectorsPerCluster);
4803 Assert(cRootDirEntries);
4804 uint32_t cbRootDir = RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector);
4805 uint32_t const cbCluster = cSectorsPerCluster * cbSector;
4806
4807 /*
4808 * If we haven't figured out the FAT type yet, do so.
4809 * The file system code determins the FAT based on cluster counts,
4810 * so we must do so here too.
4811 */
4812 if (enmFatType == RTFSFATTYPE_INVALID)
4813 {
4814 uint32_t cbFat12Overhead = cbSector /* boot sector */
4815 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
4816 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
4817 if ( cbVol <= cbFat12Overhead + cbCluster
4818 || (cbVol - cbFat12Overhead) / cbCluster <= FAT_MAX_FAT12_DATA_CLUSTERS)
4819 enmFatType = RTFSFATTYPE_FAT12;
4820 else
4821 {
4822 uint32_t cbFat16Overhead = cbSector /* boot sector */
4823 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
4824 + cbRootDir;
4825 if ( cbVol <= cbFat16Overhead + cbCluster
4826 || (cbVol - cbFat16Overhead) / cbCluster <= FAT_MAX_FAT16_DATA_CLUSTERS)
4827 enmFatType = RTFSFATTYPE_FAT16;
4828 else
4829 enmFatType = RTFSFATTYPE_FAT32;
4830 }
4831 }
4832 if (enmFatType == RTFSFATTYPE_FAT32)
4833 cbRootDir = cbCluster;
4834
4835 /*
4836 * Calculate the FAT size and number of data cluster.
4837 *
4838 * Since the FAT size depends on how many data clusters there are, we start
4839 * with a minimum FAT size and maximum clust count, then recalucate it. The
4840 * result isn't necessarily stable, so we will only retry stabalizing the
4841 * result a few times.
4842 */
4843 uint32_t cbReservedFixed = enmFatType == RTFSFATTYPE_FAT32 ? 32 * cbSector : cbSector + cbRootDir;
4844 uint32_t cbFat = cbSector;
4845 if (cbReservedFixed + cbFat * cFats >= cbVol)
4846 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
4847 cbVol, cbReservedFixed, cbFat, cFats);
4848 uint32_t cMaxClusters = enmFatType == RTFSFATTYPE_FAT12 ? FAT_MAX_FAT12_DATA_CLUSTERS
4849 : enmFatType == RTFSFATTYPE_FAT16 ? FAT_MAX_FAT16_DATA_CLUSTERS
4850 : FAT_MAX_FAT12_DATA_CLUSTERS;
4851 uint32_t cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
4852 uint32_t cPrevClusters;
4853 uint32_t cTries = 4;
4854 do
4855 {
4856 cPrevClusters = cClusters;
4857 switch (enmFatType)
4858 {
4859 case RTFSFATTYPE_FAT12:
4860 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT12_TOTAL_CLUSTERS, cClusters) * 3 / 2;
4861 break;
4862 case RTFSFATTYPE_FAT16:
4863 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT16_TOTAL_CLUSTERS, cClusters) * 2;
4864 break;
4865 case RTFSFATTYPE_FAT32:
4866 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT32_TOTAL_CLUSTERS, cClusters) * 4;
4867 cbFat = RT_ALIGN_32(cbFat, _4K);
4868 break;
4869 default:
4870 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4871 }
4872 cbFat = RT_ALIGN_32(cbFat, cbSector);
4873 if (cbReservedFixed + cbFat * cFats >= cbVol)
4874 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
4875 cbVol, cbReservedFixed, cbFat, cFats);
4876 cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
4877 } while ( cClusters != cPrevClusters
4878 && cTries-- > 0);
4879 uint64_t const cTotalSectors = cClusters * (uint64_t)cSectorsPerCluster + (cbReservedFixed + cbFat * cFats) / cbSector;
4880
4881 /*
4882 * Check that the file system type and cluster count matches up. If they
4883 * don't the type will be misdetected.
4884 *
4885 * Note! These assertions could trigger if the above calculations are wrong.
4886 */
4887 switch (enmFatType)
4888 {
4889 case RTFSFATTYPE_FAT12:
4890 AssertMsgReturn(cClusters >= FAT_MIN_FAT12_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT12_DATA_CLUSTERS,
4891 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4892 break;
4893 case RTFSFATTYPE_FAT16:
4894 AssertMsgReturn(cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT16_DATA_CLUSTERS,
4895 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4896 break;
4897 case RTFSFATTYPE_FAT32:
4898 AssertMsgReturn(cClusters >= FAT_MIN_FAT32_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS,
4899 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
4900 /* fall thru */
4901 default:
4902 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
4903 }
4904
4905 /*
4906 * Okay, create the boot sector.
4907 */
4908 size_t cbBuf = RT_MAX(RT_MAX(_64K, cbCluster), cbSector * 2U);
4909 uint8_t *pbBuf = (uint8_t *)RTMemTmpAllocZ(cbBuf);
4910 AssertReturn(pbBuf, VERR_NO_TMP_MEMORY);
4911
4912 const char *pszLastOp = "boot sector";
4913 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pbBuf;
4914 pBootSector->abJmp[0] = 0xeb;
4915 pBootSector->abJmp[1] = RT_UOFFSETOF(FATBOOTSECTOR, Bpb)
4916 + (enmFatType == RTFSFATTYPE_FAT32 ? sizeof(FAT32EBPB) : sizeof(FATEBPB)) - 2;
4917 pBootSector->abJmp[2] = 0x90;
4918 memcpy(pBootSector->achOemName, enmFatType == RTFSFATTYPE_FAT32 ? "FAT32 " : "IPRT 6.2", sizeof(pBootSector->achOemName));
4919 pBootSector->Bpb.Bpb331.cbSector = (uint16_t)cbSector;
4920 pBootSector->Bpb.Bpb331.cSectorsPerCluster = (uint8_t)cSectorsPerCluster;
4921 pBootSector->Bpb.Bpb331.cReservedSectors = enmFatType == RTFSFATTYPE_FAT32 ? cbReservedFixed / cbSector : 1;
4922 pBootSector->Bpb.Bpb331.cFats = (uint8_t)cFats;
4923 pBootSector->Bpb.Bpb331.cMaxRootDirEntries = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cRootDirEntries;
4924 pBootSector->Bpb.Bpb331.cTotalSectors16 = cTotalSectors <= UINT16_MAX ? (uint16_t)cTotalSectors : 0;
4925 pBootSector->Bpb.Bpb331.bMedia = bMedia;
4926 pBootSector->Bpb.Bpb331.cSectorsPerFat = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cbFat / cbSector;
4927 pBootSector->Bpb.Bpb331.cSectorsPerTrack = cSectorsPerTrack;
4928 pBootSector->Bpb.Bpb331.cTracksPerCylinder = cHeads;
4929 pBootSector->Bpb.Bpb331.cHiddenSectors = cHiddenSectors;
4930 pBootSector->Bpb.Bpb331.cTotalSectors32 = cTotalSectors <= UINT32_MAX ? (uint32_t)cTotalSectors : 0;
4931 if (enmFatType != RTFSFATTYPE_FAT32)
4932 {
4933 pBootSector->Bpb.Ebpb.bInt13Drive = 0;
4934 pBootSector->Bpb.Ebpb.bReserved = 0;
4935 pBootSector->Bpb.Ebpb.bExtSignature = FATEBPB_SIGNATURE;
4936 pBootSector->Bpb.Ebpb.uSerialNumber = RTRandU32();
4937 memset(pBootSector->Bpb.Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Ebpb.achLabel));
4938 memcpy(pBootSector->Bpb.Ebpb.achType, enmFatType == RTFSFATTYPE_FAT12 ? "FAT12 " : "FAT16 ",
4939 sizeof(pBootSector->Bpb.Ebpb.achType));
4940 }
4941 else
4942 {
4943 pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 = cbFat / cbSector;
4944 pBootSector->Bpb.Fat32Ebpb.fFlags = 0;
4945 pBootSector->Bpb.Fat32Ebpb.uVersion = FAT32EBPB_VERSION_0_0;
4946 pBootSector->Bpb.Fat32Ebpb.uRootDirCluster = FAT_FIRST_DATA_CLUSTER;
4947 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo = 1;
4948 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo = 6;
4949 RT_ZERO(pBootSector->Bpb.Fat32Ebpb.abReserved);
4950
4951 pBootSector->Bpb.Fat32Ebpb.bInt13Drive = 0;
4952 pBootSector->Bpb.Fat32Ebpb.bReserved = 0;
4953 pBootSector->Bpb.Fat32Ebpb.bExtSignature = FATEBPB_SIGNATURE;
4954 pBootSector->Bpb.Fat32Ebpb.uSerialNumber = RTRandU32();
4955 memset(pBootSector->Bpb.Fat32Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Fat32Ebpb.achLabel));
4956 if (cTotalSectors > UINT32_MAX)
4957 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 = cTotalSectors;
4958 else
4959 memcpy(pBootSector->Bpb.Fat32Ebpb.u.achType, "FAT32 ", sizeof(pBootSector->Bpb.Fat32Ebpb.u.achType));
4960 }
4961 pbBuf[pBootSector->abJmp[1] + 2 + 0] = 0xcd; /* int 19h */
4962 pbBuf[pBootSector->abJmp[1] + 2 + 1] = 0x19;
4963 pbBuf[pBootSector->abJmp[1] + 2 + 2] = 0xcc; /* int3 */
4964 pbBuf[pBootSector->abJmp[1] + 2 + 3] = 0xcc;
4965
4966 pBootSector->uSignature = FATBOOTSECTOR_SIGNATURE;
4967 if (cbSector != sizeof(*pBootSector))
4968 *(uint16_t *)&pbBuf[cbSector - 2] = FATBOOTSECTOR_SIGNATURE; /** @todo figure out how disks with non-512 byte sectors work! */
4969
4970 rc = RTVfsFileWriteAt(hVfsFile, offVol, pBootSector, cbSector, NULL);
4971 uint32_t const offFirstFat = pBootSector->Bpb.Bpb331.cReservedSectors * cbSector;
4972
4973 /*
4974 * Write the FAT32 info sector, 3 boot sector copies, and zero fill
4975 * the other reserved sectors.
4976 */
4977 if (RT_SUCCESS(rc) && enmFatType == RTFSFATTYPE_FAT32)
4978 {
4979 pszLastOp = "fat32 info sector";
4980 PFAT32INFOSECTOR pInfoSector = (PFAT32INFOSECTOR)&pbBuf[cbSector]; /* preserve the boot sector. */
4981 RT_ZERO(*pInfoSector);
4982 pInfoSector->uSignature1 = FAT32INFOSECTOR_SIGNATURE_1;
4983 pInfoSector->uSignature2 = FAT32INFOSECTOR_SIGNATURE_2;
4984 pInfoSector->uSignature3 = FAT32INFOSECTOR_SIGNATURE_3;
4985 pInfoSector->cFreeClusters = cClusters - 1; /* ASSUMES 1 cluster for the root dir. */
4986 pInfoSector->cLastAllocatedCluster = FAT_FIRST_DATA_CLUSTER;
4987 rc = RTVfsFileWriteAt(hVfsFile, offVol + cbSector, pInfoSector, cbSector, NULL);
4988
4989 uint32_t iSector = 2;
4990 if (RT_SUCCESS(rc))
4991 {
4992 pszLastOp = "fat32 unused reserved sectors";
4993 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
4994 (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo - iSector) * cbSector);
4995 iSector = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo;
4996 }
4997
4998 if (RT_SUCCESS(rc))
4999 {
5000 pszLastOp = "boot sector copy";
5001 for (uint32_t i = 0; i < 3 && RT_SUCCESS(rc); i++, iSector++)
5002 rc = RTVfsFileWriteAt(hVfsFile, offVol + iSector * cbSector, pBootSector, cbSector, NULL);
5003 }
5004
5005 if (RT_SUCCESS(rc))
5006 {
5007 pszLastOp = "fat32 unused reserved sectors";
5008 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
5009 (pBootSector->Bpb.Bpb331.cReservedSectors - iSector) * cbSector);
5010 }
5011 }
5012
5013 /*
5014 * The FATs.
5015 */
5016 if (RT_SUCCESS(rc))
5017 {
5018 pszLastOp = "fat";
5019 pBootSector = NULL; /* invalid */
5020 RT_BZERO(pbBuf, cbSector);
5021 switch (enmFatType)
5022 {
5023 case RTFSFATTYPE_FAT32:
5024 pbBuf[11] = 0x0f; /* EOC for root dir*/
5025 pbBuf[10] = 0xff;
5026 pbBuf[9] = 0xff;
5027 pbBuf[8] = 0xff;
5028 pbBuf[7] = 0x0f; /* Formatter's EOC, followed by signed extend FAT ID. */
5029 pbBuf[6] = 0xff;
5030 pbBuf[5] = 0xff;
5031 pbBuf[4] = 0xff;
5032 /* fall thru */
5033 case RTFSFATTYPE_FAT16:
5034 pbBuf[3] = 0xff;
5035 /* fall thru */
5036 case RTFSFATTYPE_FAT12:
5037 pbBuf[2] = 0xff;
5038 pbBuf[1] = 0xff;
5039 pbBuf[0] = bMedia; /* FAT ID */
5040 break;
5041 default: AssertFailed();
5042 }
5043 for (uint32_t iFatCopy = 0; iFatCopy < cFats && RT_SUCCESS(rc); iFatCopy++)
5044 {
5045 rc = RTVfsFileWriteAt(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy, pbBuf, cbSector, NULL);
5046 if (RT_SUCCESS(rc) && cbFat > cbSector)
5047 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy + cbSector, cbFat - cbSector);
5048 }
5049 }
5050
5051 /*
5052 * The root directory.
5053 */
5054 if (RT_SUCCESS(rc))
5055 {
5056 /** @todo any mandatory directory entries we need to fill in here? */
5057 pszLastOp = "root dir";
5058 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * cFats, cbRootDir);
5059 }
5060
5061 /*
5062 * If long format, fill the rest of the disk with 0xf6.
5063 */
5064 AssertCompile(RTFSFATVOL_FMT_F_QUICK != 0);
5065 if (RT_SUCCESS(rc) && !(fFlags & RTFSFATVOL_FMT_F_QUICK))
5066 {
5067 pszLastOp = "formatting data clusters";
5068 uint64_t offCur = offFirstFat + cbFat * cFats + cbRootDir;
5069 uint64_t cbLeft = cTotalSectors * cbSector;
5070 if (cbLeft > offCur)
5071 {
5072 cbLeft -= offCur;
5073 offCur += offVol;
5074
5075 memset(pbBuf, 0xf6, cbBuf);
5076 while (cbLeft > 0)
5077 {
5078 size_t cbToWrite = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
5079 rc = RTVfsFileWriteAt(hVfsFile, offCur, pbBuf, cbToWrite, NULL);
5080 if (RT_SUCCESS(rc))
5081 {
5082 offCur += cbToWrite;
5083 cbLeft -= cbToWrite;
5084 }
5085 else
5086 break;
5087 }
5088 }
5089 }
5090
5091 /*
5092 * Done.
5093 */
5094 RTMemTmpFree(pbBuf);
5095 if (RT_SUCCESS(rc))
5096 return rc;
5097 return RTErrInfoSet(pErrInfo, rc, pszLastOp);
5098}
5099
5100
5101/**
5102 * Formats a 1.44MB floppy image.
5103 *
5104 * @returns IPRT status code.
5105 * @param hVfsFile The image.
5106 */
5107RTDECL(int) RTFsFatVolFormat144(RTVFSFILE hVfsFile, bool fQuick)
5108{
5109 return RTFsFatVolFormat(hVfsFile, 0 /*offVol*/, 1474560, fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
5110 512 /*cbSector*/, 2 /*cSectorsPerCluster*/, RTFSFATTYPE_FAT12, 2 /*cHeads*/, 18 /*cSectors*/,
5111 0xf0 /*bMedia*/, 0 /*cHiddenSectors*/, 224 /*cRootDirEntries*/, NULL /*pErrInfo*/);
5112}
5113
5114
5115/**
5116 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
5117 */
5118static DECLCALLBACK(int) rtVfsChainFatVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
5119 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
5120{
5121 RT_NOREF(pProviderReg);
5122
5123 /*
5124 * Basic checks.
5125 */
5126 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
5127 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
5128 if ( pElement->enmType != RTVFSOBJTYPE_VFS
5129 && pElement->enmType != RTVFSOBJTYPE_DIR)
5130 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
5131 if (pElement->cArgs > 1)
5132 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
5133
5134 /*
5135 * Parse the flag if present, save in pElement->uProvider.
5136 */
5137 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
5138 if (pElement->cArgs > 0)
5139 {
5140 const char *psz = pElement->paArgs[0].psz;
5141 if (*psz)
5142 {
5143 if (!strcmp(psz, "ro"))
5144 fReadOnly = true;
5145 else if (!strcmp(psz, "rw"))
5146 fReadOnly = false;
5147 else
5148 {
5149 *poffError = pElement->paArgs[0].offSpec;
5150 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
5151 }
5152 }
5153 }
5154
5155 pElement->uProvider = fReadOnly;
5156 return VINF_SUCCESS;
5157}
5158
5159
5160/**
5161 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
5162 */
5163static DECLCALLBACK(int) rtVfsChainFatVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
5164 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
5165 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
5166{
5167 RT_NOREF(pProviderReg, pSpec, poffError);
5168
5169 int rc;
5170 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
5171 if (hVfsFileIn != NIL_RTVFSFILE)
5172 {
5173 RTVFS hVfs;
5174 rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, 0, &hVfs, pErrInfo);
5175 RTVfsFileRelease(hVfsFileIn);
5176 if (RT_SUCCESS(rc))
5177 {
5178 *phVfsObj = RTVfsObjFromVfs(hVfs);
5179 RTVfsRelease(hVfs);
5180 if (*phVfsObj != NIL_RTVFSOBJ)
5181 return VINF_SUCCESS;
5182 rc = VERR_VFS_CHAIN_CAST_FAILED;
5183 }
5184 }
5185 else
5186 rc = VERR_VFS_CHAIN_CAST_FAILED;
5187 return rc;
5188}
5189
5190
5191/**
5192 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
5193 */
5194static DECLCALLBACK(bool) rtVfsChainFatVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
5195 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
5196 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
5197{
5198 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
5199 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
5200 || !pReuseElement->paArgs[0].uProvider)
5201 return true;
5202 return false;
5203}
5204
5205
5206/** VFS chain element 'file'. */
5207static RTVFSCHAINELEMENTREG g_rtVfsChainFatVolReg =
5208{
5209 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
5210 /* fReserved = */ 0,
5211 /* pszName = */ "fat",
5212 /* ListEntry = */ { NULL, NULL },
5213 /* pszHelp = */ "Open a FAT file system, requires a file object on the left side.\n"
5214 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
5215 /* pfnValidate = */ rtVfsChainFatVol_Validate,
5216 /* pfnInstantiate = */ rtVfsChainFatVol_Instantiate,
5217 /* pfnCanReuseElement = */ rtVfsChainFatVol_CanReuseElement,
5218 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
5219};
5220
5221RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainFatVolReg, rtVfsChainFatVolReg);
5222
Note: See TracBrowser for help on using the repository browser.

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