VirtualBox

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

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

fatvfs.cpp: Split dir and file structures into shared and handle specific parts, like the ifsvfs does. Docs and nits.

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