VirtualBox

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

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

fatvfs: Relaxed idxEndOfChain (FAT entry #2) checks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 215.2 KB
Line 
1/* $Id: fatvfs.cpp 69619 2017-11-08 15:46:10Z 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 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3670 PRTFSFATDIRSHRD pShared = pThis->pShared;
3671 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
3672
3673 /*
3674 * Try open directory.
3675 */
3676 uint32_t offEntryInDir;
3677 bool fLong;
3678 FATDIRENTRY DirEntry;
3679 int rc = rtFsFatDirShrd_FindEntry(pShared, pszSubDir, &offEntryInDir, &fLong, &DirEntry);
3680 LogFlow(("rtFsFatDir_OpenDir: FindEntry(,%s,,,) -> %Rrc fLong=%d offEntryInDir=%#RX32\n", pszSubDir, rc, fLong, offEntryInDir));
3681 if (RT_SUCCESS(rc))
3682 {
3683 switch (DirEntry.fAttrib & (FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME))
3684 {
3685 case FAT_ATTR_DIRECTORY:
3686 rc = rtFsFatDir_New(pShared->Core.pVol, pShared, &DirEntry, offEntryInDir,
3687 RTFSFAT_GET_CLUSTER(&DirEntry, pShared->Core.pVol), UINT64_MAX /*offDisk*/,
3688 DirEntry.cbFile, phVfsDir);
3689 break;
3690
3691 case 0:
3692 rc = VERR_NOT_A_DIRECTORY;
3693 break;
3694
3695 default:
3696 rc = VERR_PATH_NOT_FOUND;
3697 break;
3698 }
3699 }
3700 return rc;
3701}
3702
3703
3704/**
3705 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3706 */
3707static DECLCALLBACK(int) rtFsFatDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3708{
3709 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3710 PRTFSFATDIRSHRD pShared = pThis->pShared;
3711 RT_NOREF(fMode);
3712
3713 /*
3714 * Check if it already exists in any form.
3715 */
3716 uint32_t offEntryInDir;
3717 bool fLong;
3718 FATDIRENTRY DirEntry;
3719 int rc = rtFsFatDirShrd_FindEntry(pShared, pszSubDir, &offEntryInDir, &fLong, &DirEntry);
3720 if (rc != VERR_FILE_NOT_FOUND)
3721 return RT_SUCCESS(rc) ? VERR_ALREADY_EXISTS : rc;
3722
3723 /*
3724 * Okay, create it.
3725 */
3726 rc = rtFsFatDirShrd_CreateEntry(pShared, pszSubDir, FAT_ATTR_ARCHIVE | FAT_ATTR_DIRECTORY,
3727 pShared->Core.pVol->cbCluster, &offEntryInDir, &DirEntry);
3728 if (RT_SUCCESS(rc))
3729 rc = rtFsFatDir_New(pShared->Core.pVol, pShared, &DirEntry, offEntryInDir,
3730 RTFSFAT_GET_CLUSTER(&DirEntry, pShared->Core.pVol), UINT64_MAX /*offDisk*/,
3731 DirEntry.cbFile, phVfsDir);
3732 return rc;
3733}
3734
3735
3736/**
3737 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3738 */
3739static DECLCALLBACK(int) rtFsFatDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3740{
3741 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3742 return VERR_NOT_SUPPORTED;
3743}
3744
3745
3746/**
3747 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3748 */
3749static DECLCALLBACK(int) rtFsFatDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3750 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3751{
3752 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3753 return VERR_NOT_SUPPORTED;
3754}
3755
3756
3757/**
3758 * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
3759 */
3760static DECLCALLBACK(int) rtFsFatDir_QueryEntryInfo(void *pvThis, const char *pszEntry,
3761 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3762{
3763 /*
3764 * Try locate the entry.
3765 */
3766 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3767 PRTFSFATDIRSHRD pShared = pThis->pShared;
3768 uint32_t offEntryInDir;
3769 bool fLong;
3770 FATDIRENTRY DirEntry;
3771 int rc = rtFsFatDirShrd_FindEntry(pShared, pszEntry, &offEntryInDir, &fLong, &DirEntry);
3772 Log2(("rtFsFatDir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
3773 if (RT_SUCCESS(rc))
3774 {
3775 /*
3776 * To avoid duplicating code in rtFsFatObj_InitFromDirRec and
3777 * rtFsFatObj_QueryInfo, we create a dummy RTFSFATOBJ on the stack.
3778 */
3779 RTFSFATOBJ TmpObj;
3780 RT_ZERO(TmpObj);
3781 rtFsFatObj_InitFromDirEntry(&TmpObj, &DirEntry, offEntryInDir, pShared->Core.pVol);
3782 rc = rtFsFatObj_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
3783 }
3784 return rc;
3785}
3786
3787
3788/**
3789 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3790 */
3791static DECLCALLBACK(int) rtFsFatDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3792{
3793 RT_NOREF(pvThis, pszEntry, fType);
3794 return VERR_NOT_IMPLEMENTED;
3795}
3796
3797
3798/**
3799 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3800 */
3801static DECLCALLBACK(int) rtFsFatDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3802{
3803 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3804 return VERR_NOT_IMPLEMENTED;
3805}
3806
3807
3808/**
3809 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3810 */
3811static DECLCALLBACK(int) rtFsFatDir_RewindDir(void *pvThis)
3812{
3813 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3814 pThis->offDir = 0;
3815 return VINF_SUCCESS;
3816}
3817
3818
3819/**
3820 * Calculates the UTF-8 length of the name in the given directory entry.
3821 *
3822 * @returns The length in characters (bytes), excluding terminator.
3823 * @param pShared The shared directory structure (for codepage).
3824 * @param pEntry The directory entry.
3825 */
3826static size_t rtFsFatDir_CalcUtf8LengthForDirEntry(PRTFSFATDIRSHRD pShared, PCFATDIRENTRY pEntry)
3827{
3828 RT_NOREF(pShared);
3829 PCRTUTF16 g_pawcMap = &g_awchFatCp437Chars[0];
3830
3831 /* The base name (this won't work with DBCS, but that's not a concern at the moment). */
3832 size_t offSrc = 8;
3833 while (offSrc > 1 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[offSrc - 1]]))
3834 offSrc--;
3835
3836 size_t cchRet = 0;
3837 while (offSrc-- > 0)
3838 cchRet += RTStrCpSize(g_pawcMap[pEntry->achName[offSrc]]);
3839
3840 /* Extension. */
3841 offSrc = 11;
3842 while (offSrc > 8 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[offSrc - 1]]))
3843 offSrc--;
3844 if (offSrc > 8)
3845 {
3846 cchRet += 1; /* '.' */
3847 while (offSrc-- > 8)
3848 cchRet += RTStrCpSize(g_pawcMap[pEntry->achName[offSrc]]);
3849 }
3850
3851 return cchRet;
3852}
3853
3854
3855/**
3856 * Copies the name from the directory entry into a UTF-16 buffer.
3857 *
3858 * @returns Number of UTF-16 items written (excluding terminator).
3859 * @param pShared The shared directory structure (for codepage).
3860 * @param pEntry The directory entry.
3861 * @param pwszDst The destination buffer.
3862 * @param cwcDst The destination buffer size.
3863 */
3864static uint16_t rtFsFatDir_CopyDirEntryToUtf16(PRTFSFATDIRSHRD pShared, PCFATDIRENTRY pEntry, PRTUTF16 pwszDst, size_t cwcDst)
3865{
3866 Assert(cwcDst > 0);
3867
3868 RT_NOREF(pShared);
3869 PCRTUTF16 g_pawcMap = &g_awchFatCp437Chars[0];
3870
3871 /* The base name (this won't work with DBCS, but that's not a concern at the moment). */
3872 size_t cchSrc = 8;
3873 while (cchSrc > 1 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[cchSrc - 1]]))
3874 cchSrc--;
3875
3876 size_t offDst = 0;
3877 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
3878 {
3879 AssertReturnStmt(offDst + 1 < cwcDst, pwszDst[cwcDst - 1] = '\0', (uint16_t)cwcDst);
3880 pwszDst[offDst++] = g_pawcMap[pEntry->achName[offSrc]];
3881 }
3882
3883 /* Extension. */
3884 cchSrc = 3;
3885 while (cchSrc > 0 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[8 + cchSrc - 1]]))
3886 cchSrc--;
3887 if (cchSrc > 0)
3888 {
3889 AssertReturnStmt(offDst + 1 < cwcDst, pwszDst[cwcDst - 1] = '\0', (uint16_t)cwcDst);
3890 pwszDst[offDst++] = '.';
3891
3892 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
3893 {
3894 AssertReturnStmt(offDst + 1 < cwcDst, pwszDst[cwcDst - 1] = '\0', (uint16_t)cwcDst);
3895 pwszDst[offDst++] = g_pawcMap[pEntry->achName[8 + offSrc]];
3896 }
3897 }
3898
3899 pwszDst[offDst] = '\0';
3900 return (uint16_t)offDst;
3901}
3902
3903
3904/**
3905 * Copies the name from the directory entry into a UTF-8 buffer.
3906 *
3907 * @returns Number of UTF-16 items written (excluding terminator).
3908 * @param pShared The shared directory structure (for codepage).
3909 * @param pEntry The directory entry.
3910 * @param pszDst The destination buffer.
3911 * @param cbDst The destination buffer size.
3912 */
3913static uint16_t rtFsFatDir_CopyDirEntryToUtf8(PRTFSFATDIRSHRD pShared, PCFATDIRENTRY pEntry, char *pszDst, size_t cbDst)
3914{
3915 Assert(cbDst > 0);
3916
3917 RT_NOREF(pShared);
3918 PCRTUTF16 g_pawcMap = &g_awchFatCp437Chars[0];
3919
3920 /* The base name (this won't work with DBCS, but that's not a concern at the moment). */
3921 size_t cchSrc = 8;
3922 while (cchSrc > 1 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[cchSrc - 1]]))
3923 cchSrc--;
3924
3925 char * const pszDstEnd = pszDst + cbDst;
3926 char *pszCurDst = pszDst;
3927 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
3928 {
3929 RTUNICP const uc = g_pawcMap[pEntry->achName[offSrc]];
3930 size_t cbCp = RTStrCpSize(uc);
3931 AssertReturnStmt(cbCp < (size_t)(pszDstEnd - pszCurDst), *pszCurDst = '\0', (uint16_t)(pszDstEnd - pszCurDst));
3932 pszCurDst = RTStrPutCp(pszCurDst, uc);
3933 }
3934
3935 /* Extension. */
3936 cchSrc = 3;
3937 while (cchSrc > 0 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[8 + cchSrc - 1]]))
3938 cchSrc--;
3939 if (cchSrc > 0)
3940 {
3941 AssertReturnStmt(1U < (size_t)(pszDstEnd - pszCurDst), *pszCurDst = '\0', (uint16_t)(pszDstEnd - pszCurDst));
3942 *pszCurDst++ = '.';
3943
3944 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
3945 {
3946 RTUNICP const uc = g_pawcMap[pEntry->achName[8 + offSrc]];
3947 size_t cbCp = RTStrCpSize(uc);
3948 AssertReturnStmt(cbCp < (size_t)(pszDstEnd - pszCurDst), *pszCurDst = '\0', (uint16_t)(pszDstEnd - pszCurDst));
3949 pszCurDst = RTStrPutCp(pszCurDst, uc);
3950 }
3951 }
3952
3953 *pszCurDst = '\0';
3954 return (uint16_t)(pszDstEnd - pszCurDst);
3955}
3956
3957
3958/**
3959 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3960 */
3961static DECLCALLBACK(int) rtFsFatDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3962 RTFSOBJATTRADD enmAddAttr)
3963{
3964 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
3965 PRTFSFATDIRSHRD pShared = pThis->pShared;
3966
3967 /*
3968 * Fake '.' and '..' entries.
3969 */
3970 if (pThis->offDir < 2)
3971 {
3972 size_t cbNeeded = RT_OFFSETOF(RTDIRENTRYEX, szName[pThis->offDir + 2]);
3973 if (cbNeeded < *pcbDirEntry)
3974 *pcbDirEntry = cbNeeded;
3975 else
3976 {
3977 *pcbDirEntry = cbNeeded;
3978 return VERR_BUFFER_OVERFLOW;
3979 }
3980
3981 int rc;
3982 if ( pThis->offDir == 0
3983 || pShared->Core.pParentDir == NULL)
3984 rc = rtFsFatObj_QueryInfo(&pShared->Core, &pDirEntry->Info, enmAddAttr);
3985 else
3986 rc = rtFsFatObj_QueryInfo(&pShared->Core.pParentDir->Core, &pDirEntry->Info, enmAddAttr);
3987
3988 pDirEntry->cwcShortName = 0;
3989 pDirEntry->wszShortName[0] = '\0';
3990 pDirEntry->szName[0] = '.';
3991 pDirEntry->szName[1] = '.';
3992 pDirEntry->szName[++pThis->offDir] = '\0';
3993 pDirEntry->cbName = pThis->offDir;
3994 return rc;
3995 }
3996
3997 /*
3998 * Scan the directory buffer by buffer.
3999 */
4000 RTUTF16 wszName[260+1];
4001 uint8_t bChecksum = UINT8_MAX;
4002 uint8_t idNextSlot = UINT8_MAX;
4003 size_t cwcName = 0;
4004 uint32_t offEntryInDir = pThis->offDir - 2;
4005 uint32_t const cbDir = pShared->Core.cbObject;
4006 Assert(RT_ALIGN_32(cbDir, sizeof(*pDirEntry)) == cbDir);
4007 AssertCompile(FATDIRNAMESLOT_MAX_SLOTS * FATDIRNAMESLOT_CHARS_PER_SLOT < RT_ELEMENTS(wszName));
4008 wszName[260] = '\0';
4009
4010 while (offEntryInDir < cbDir)
4011 {
4012 /* Get chunk of entries starting at offEntryInDir. */
4013 uint32_t uBufferLock = UINT32_MAX;
4014 uint32_t cEntries = 0;
4015 PCFATDIRENTRYUNION paEntries = NULL;
4016 int rc = rtFsFatDirShrd_GetEntriesAt(pShared, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
4017 if (RT_FAILURE(rc))
4018 return rc;
4019
4020 /*
4021 * Now work thru each of the entries.
4022 */
4023 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
4024 {
4025 switch ((uint8_t)paEntries[iEntry].Entry.achName[0])
4026 {
4027 default:
4028 break;
4029 case FATDIRENTRY_CH0_DELETED:
4030 cwcName = 0;
4031 continue;
4032 case FATDIRENTRY_CH0_END_OF_DIR:
4033 if (pShared->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
4034 {
4035 pThis->offDir = cbDir + 2;
4036 rtFsFatDirShrd_ReleaseBufferAfterReading(pShared, uBufferLock);
4037 return VERR_NO_MORE_FILES;
4038 }
4039 cwcName = 0;
4040 break; /* Technically a valid entry before DOS 2.0, or so some claim. */
4041 }
4042
4043 /*
4044 * Check for long filename slot.
4045 */
4046 if ( paEntries[iEntry].Slot.fAttrib == FAT_ATTR_NAME_SLOT
4047 && paEntries[iEntry].Slot.idxZero == 0
4048 && paEntries[iEntry].Slot.fZero == 0
4049 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) <= FATDIRNAMESLOT_HIGHEST_SLOT_ID
4050 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) != 0)
4051 {
4052 /* New slot? */
4053 if (paEntries[iEntry].Slot.idSlot & FATDIRNAMESLOT_FIRST_SLOT_FLAG)
4054 {
4055 idNextSlot = paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG;
4056 bChecksum = paEntries[iEntry].Slot.bChecksum;
4057 cwcName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
4058 wszName[cwcName] = '\0';
4059 }
4060 /* Is valid next entry? */
4061 else if ( paEntries[iEntry].Slot.idSlot == idNextSlot
4062 && paEntries[iEntry].Slot.bChecksum == bChecksum)
4063 { /* likely */ }
4064 else
4065 cwcName = 0;
4066 if (cwcName)
4067 {
4068 idNextSlot--;
4069 size_t offName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
4070 memcpy(&wszName[offName], paEntries[iEntry].Slot.awcName0, sizeof(paEntries[iEntry].Slot.awcName0));
4071 memcpy(&wszName[offName + 5], paEntries[iEntry].Slot.awcName1, sizeof(paEntries[iEntry].Slot.awcName1));
4072 memcpy(&wszName[offName + 5 + 6], paEntries[iEntry].Slot.awcName2, sizeof(paEntries[iEntry].Slot.awcName2));
4073 }
4074 }
4075 /*
4076 * Got a regular directory entry. Try return it to the caller.
4077 */
4078 else
4079 {
4080 /* Do the length calc and check for overflows. */
4081 bool fLongName = false;
4082 size_t cchName = 0;
4083 if ( cwcName != 0
4084 && idNextSlot == 0
4085 && rtFsFatDir_CalcChecksum(&paEntries[iEntry].Entry) == bChecksum)
4086 {
4087 rc = RTUtf16CalcUtf8LenEx(wszName, cwcName, &cchName);
4088 if (RT_FAILURE(rc))
4089 {
4090 fLongName = false;
4091 cwcName = 0;
4092 }
4093 }
4094 if (!fLongName)
4095 cchName = rtFsFatDir_CalcUtf8LengthForDirEntry(pShared, &paEntries[iEntry].Entry);
4096 size_t cbNeeded = RT_OFFSETOF(RTDIRENTRYEX, szName[cchName + 1]);
4097 if (cbNeeded <= *pcbDirEntry)
4098 *pcbDirEntry = cbNeeded;
4099 else
4100 {
4101 *pcbDirEntry = cbNeeded;
4102 return VERR_BUFFER_OVERFLOW;
4103 }
4104
4105 /* To avoid duplicating code in rtFsFatObj_InitFromDirRec and
4106 rtFsFatObj_QueryInfo, we create a dummy RTFSFATOBJ on the stack. */
4107 RTFSFATOBJ TmpObj;
4108 RT_ZERO(TmpObj);
4109 rtFsFatObj_InitFromDirEntry(&TmpObj, &paEntries[iEntry].Entry, offEntryInDir, pShared->Core.pVol);
4110
4111 rtFsFatDirShrd_ReleaseBufferAfterReading(pShared, uBufferLock);
4112
4113 rc = rtFsFatObj_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
4114
4115 /* Copy out the names. */
4116 pDirEntry->cbName = (uint16_t)cchName;
4117 if (fLongName)
4118 {
4119 char *pszDst = &pDirEntry->szName[0];
4120 int rc2 = RTUtf16ToUtf8Ex(wszName, cwcName, &pszDst, cchName + 1, NULL);
4121 AssertRC(rc2);
4122
4123 pDirEntry->cwcShortName = rtFsFatDir_CopyDirEntryToUtf16(pShared, &paEntries[iEntry].Entry,
4124 pDirEntry->wszShortName,
4125 RT_ELEMENTS(pDirEntry->wszShortName));
4126 }
4127 else
4128 {
4129 rtFsFatDir_CopyDirEntryToUtf8(pShared, &paEntries[iEntry].Entry, &pDirEntry->szName[0], cchName + 1);
4130 pDirEntry->wszShortName[0] = '\0';
4131 pDirEntry->cwcShortName = 0;
4132 }
4133
4134 if (RT_SUCCESS(rc))
4135 pThis->offDir = offEntryInDir + sizeof(paEntries[iEntry]) + 2;
4136 return rc;
4137 }
4138 }
4139
4140 rtFsFatDirShrd_ReleaseBufferAfterReading(pShared, uBufferLock);
4141 }
4142
4143 pThis->offDir = cbDir + 2;
4144 return VERR_NO_MORE_FILES;
4145}
4146
4147
4148/**
4149 * FAT directory operations.
4150 */
4151static const RTVFSDIROPS g_rtFsFatDirOps =
4152{
4153 { /* Obj */
4154 RTVFSOBJOPS_VERSION,
4155 RTVFSOBJTYPE_DIR,
4156 "FatDir",
4157 rtFsFatDir_Close,
4158 rtFsFatDir_QueryInfo,
4159 RTVFSOBJOPS_VERSION
4160 },
4161 RTVFSDIROPS_VERSION,
4162 0,
4163 { /* ObjSet */
4164 RTVFSOBJSETOPS_VERSION,
4165 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
4166 rtFsFatDir_SetMode,
4167 rtFsFatDir_SetTimes,
4168 rtFsFatDir_SetOwner,
4169 RTVFSOBJSETOPS_VERSION
4170 },
4171 rtFsFatDir_TraversalOpen,
4172 rtFsFatDir_OpenFile,
4173 rtFsFatDir_OpenDir,
4174 rtFsFatDir_CreateDir,
4175 rtFsFatDir_OpenSymlink,
4176 rtFsFatDir_CreateSymlink,
4177 rtFsFatDir_QueryEntryInfo,
4178 rtFsFatDir_UnlinkEntry,
4179 rtFsFatDir_RenameEntry,
4180 rtFsFatDir_RewindDir,
4181 rtFsFatDir_ReadDir,
4182 RTVFSDIROPS_VERSION,
4183};
4184
4185
4186
4187
4188/**
4189 * Adds an open child to the parent directory.
4190 *
4191 * Maintains an additional reference to the parent dir to prevent it from going
4192 * away. If @a pDir is the root directory, it also ensures the volume is
4193 * referenced and sticks around until the last open object is gone.
4194 *
4195 * @param pDir The directory.
4196 * @param pChild The child being opened.
4197 * @sa rtFsFatDirShrd_RemoveOpenChild
4198 */
4199static void rtFsFatDirShrd_AddOpenChild(PRTFSFATDIRSHRD pDir, PRTFSFATOBJ pChild)
4200{
4201 rtFsFatDirShrd_Retain(pDir);
4202
4203 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
4204 pChild->pParentDir = pDir;
4205}
4206
4207
4208/**
4209 * Removes an open child to the parent directory.
4210 *
4211 * @param pDir The directory.
4212 * @param pChild The child being removed.
4213 *
4214 * @remarks This is the very last thing you do as it may cause a few other
4215 * objects to be released recursively (parent dir and the volume).
4216 *
4217 * @sa rtFsFatDirShrd_AddOpenChild
4218 */
4219static void rtFsFatDirShrd_RemoveOpenChild(PRTFSFATDIRSHRD pDir, PRTFSFATOBJ pChild)
4220{
4221 AssertReturnVoid(pChild->pParentDir == pDir);
4222 RTListNodeRemove(&pChild->Entry);
4223 pChild->pParentDir = NULL;
4224
4225 rtFsFatDirShrd_Release(pDir);
4226}
4227
4228
4229/**
4230 * Instantiates a new shared directory instance.
4231 *
4232 * @returns IPRT status code.
4233 * @param pThis The FAT volume instance.
4234 * @param pParentDir The parent directory. This is NULL for the root
4235 * directory.
4236 * @param pDirEntry The parent directory entry. This is NULL for the
4237 * root directory.
4238 * @param offEntryInDir The byte offset of the directory entry in the parent
4239 * directory. UINT32_MAX if root directory.
4240 * @param idxCluster The cluster where the directory content is to be
4241 * found. This can be UINT32_MAX if a root FAT12/16
4242 * directory.
4243 * @param offDisk The disk byte offset of the FAT12/16 root directory.
4244 * This is UINT64_MAX if idxCluster is given.
4245 * @param cbDir The size of the directory.
4246 * @param ppSharedDir Where to return shared FAT directory instance.
4247 */
4248static int rtFsFatDirShrd_New(PRTFSFATVOL pThis, PRTFSFATDIRSHRD pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
4249 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTFSFATDIRSHRD *ppSharedDir)
4250{
4251 Assert((idxCluster == UINT32_MAX) != (offDisk == UINT64_MAX));
4252 Assert((pDirEntry == NULL) == (offEntryInDir == UINT32_MAX));
4253 *ppSharedDir = NULL;
4254
4255 int rc = VERR_NO_MEMORY;
4256 PRTFSFATDIRSHRD pShared = (PRTFSFATDIRSHRD)RTMemAllocZ(sizeof(*pShared));
4257 if (pShared)
4258 {
4259 /*
4260 * Initialize it all so rtFsFatDir_Close doesn't trip up in anyway.
4261 */
4262 RTListInit(&pShared->OpenChildren);
4263 if (pDirEntry)
4264 rtFsFatObj_InitFromDirEntry(&pShared->Core, pDirEntry, offEntryInDir, pThis);
4265 else
4266 rtFsFatObj_InitDummy(&pShared->Core, cbDir, RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_UNIX_ALL_PERMS, pThis);
4267
4268 pShared->cEntries = cbDir / sizeof(FATDIRENTRY);
4269 pShared->fIsLinearRootDir = idxCluster == UINT32_MAX;
4270 pShared->fFullyBuffered = pShared->fIsLinearRootDir;
4271 pShared->paEntries = NULL;
4272 pShared->offEntriesOnDisk = UINT64_MAX;
4273 if (pShared->fFullyBuffered)
4274 pShared->cbAllocatedForEntries = RT_ALIGN_32(cbDir, pThis->cbSector);
4275 else
4276 pShared->cbAllocatedForEntries = pThis->cbSector;
4277
4278 /*
4279 * If clustered backing, read the chain and see if we cannot still do the full buffering.
4280 */
4281 if (idxCluster != UINT32_MAX)
4282 {
4283 rc = rtFsFatClusterMap_ReadClusterChain(pThis, idxCluster, &pShared->Core.Clusters);
4284 if (RT_SUCCESS(rc))
4285 {
4286 if ( pShared->Core.Clusters.cClusters >= 1
4287 && pShared->Core.Clusters.cbChain <= _64K
4288 && rtFsFatChain_IsContiguous(&pShared->Core.Clusters))
4289 {
4290 Assert(pShared->Core.Clusters.cbChain >= cbDir);
4291 pShared->cbAllocatedForEntries = pShared->Core.Clusters.cbChain;
4292 pShared->fFullyBuffered = true;
4293 }
4294 }
4295 }
4296 else
4297 {
4298 rtFsFatChain_InitEmpty(&pShared->Core.Clusters, pThis);
4299 rc = VINF_SUCCESS;
4300 }
4301 if (RT_SUCCESS(rc))
4302 {
4303 /*
4304 * Allocate and initialize the buffering. Fill the buffer.
4305 */
4306 pShared->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pShared->cbAllocatedForEntries);
4307 if (!pShared->paEntries)
4308 {
4309 if (pShared->fFullyBuffered && !pShared->fIsLinearRootDir)
4310 {
4311 pShared->fFullyBuffered = false;
4312 pShared->cbAllocatedForEntries = pThis->cbSector;
4313 pShared->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pShared->cbAllocatedForEntries);
4314 }
4315 if (!pShared->paEntries)
4316 rc = VERR_NO_MEMORY;
4317 }
4318
4319 if (RT_SUCCESS(rc))
4320 {
4321 if (pShared->fFullyBuffered)
4322 {
4323 pShared->u.Full.cDirtySectors = 0;
4324 pShared->u.Full.cSectors = pShared->cbAllocatedForEntries / pThis->cbSector;
4325 pShared->u.Full.pbDirtySectors = (uint8_t *)RTMemAllocZ((pShared->u.Full.cSectors + 63) / 8);
4326 if (pShared->u.Full.pbDirtySectors)
4327 pShared->offEntriesOnDisk = offDisk != UINT64_MAX ? offDisk
4328 : rtFsFatClusterToDiskOffset(pThis, idxCluster);
4329 else
4330 rc = VERR_NO_MEMORY;
4331 }
4332 else
4333 {
4334 pShared->offEntriesOnDisk = rtFsFatClusterToDiskOffset(pThis, idxCluster);
4335 pShared->u.Simple.offInDir = 0;
4336 pShared->u.Simple.fDirty = false;
4337 }
4338 if (RT_SUCCESS(rc))
4339 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->offEntriesOnDisk,
4340 pShared->paEntries, pShared->cbAllocatedForEntries, NULL);
4341 if (RT_SUCCESS(rc))
4342 {
4343 /*
4344 * Link into parent directory so we can use it to update
4345 * our directory entry.
4346 */
4347 if (pParentDir)
4348 rtFsFatDirShrd_AddOpenChild(pParentDir, &pShared->Core);
4349 *ppSharedDir = pShared;
4350 return VINF_SUCCESS;
4351 }
4352 }
4353
4354 /* Free the buffer on failure so rtFsFatDir_Close doesn't try do anything with it. */
4355 RTMemFree(pShared->paEntries);
4356 pShared->paEntries = NULL;
4357 }
4358
4359 Assert(pShared->Core.cRefs == 1);
4360 rtFsFatDirShrd_Release(pShared);
4361 }
4362 return rc;
4363}
4364
4365
4366/**
4367 * Instantiates a new directory with a shared structure presupplied.
4368 *
4369 * @returns IPRT status code.
4370 * @param pThis The FAT volume instance.
4371 * @param pShared Referenced pointer to the shared structure. The
4372 * reference is always CONSUMED.
4373 * @param phVfsDir Where to return the directory handle.
4374 */
4375static int rtFsFatDir_NewWithShared(PRTFSFATVOL pThis, PRTFSFATDIRSHRD pShared, PRTVFSDIR phVfsDir)
4376{
4377 /*
4378 * Create VFS object around the shared structure.
4379 */
4380 PRTFSFATDIR pNewDir;
4381 int rc = RTVfsNewDir(&g_rtFsFatDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
4382 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
4383 if (RT_SUCCESS(rc))
4384 {
4385 /*
4386 * Look for existing shared object, create a new one if necessary.
4387 * We CONSUME a reference to pShared here.
4388 */
4389 pNewDir->offDir = 0;
4390 pNewDir->pShared = pShared;
4391 return VINF_SUCCESS;
4392 }
4393
4394 rtFsFatDirShrd_Release(pShared);
4395 *phVfsDir = NIL_RTVFSDIR;
4396 return rc;
4397}
4398
4399
4400
4401/**
4402 * Instantiates a new directory VFS, creating the shared structure as necessary.
4403 *
4404 * @returns IPRT status code.
4405 * @param pThis The FAT volume instance.
4406 * @param pParentDir The parent directory. This is NULL for the root
4407 * directory.
4408 * @param pDirEntry The parent directory entry. This is NULL for the
4409 * root directory.
4410 * @param offEntryInDir The byte offset of the directory entry in the parent
4411 * directory. UINT32_MAX if root directory.
4412 * @param idxCluster The cluster where the directory content is to be
4413 * found. This can be UINT32_MAX if a root FAT12/16
4414 * directory.
4415 * @param offDisk The disk byte offset of the FAT12/16 root directory.
4416 * This is UINT64_MAX if idxCluster is given.
4417 * @param cbDir The size of the directory.
4418 * @param phVfsDir Where to return the directory handle.
4419 */
4420static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIRSHRD pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
4421 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir)
4422{
4423 /*
4424 * Look for existing shared object, create a new one if necessary.
4425 */
4426 PRTFSFATDIRSHRD pShared = (PRTFSFATDIRSHRD)rtFsFatDirShrd_LookupShared(pParentDir, offEntryInDir);
4427 if (!pShared)
4428 {
4429 int rc = rtFsFatDirShrd_New(pThis, pParentDir, pDirEntry, offEntryInDir, idxCluster, offDisk, cbDir, &pShared);
4430 if (RT_FAILURE(rc))
4431 {
4432 *phVfsDir = NIL_RTVFSDIR;
4433 return rc;
4434 }
4435 }
4436 return rtFsFatDir_NewWithShared(pThis, pShared, phVfsDir);
4437}
4438
4439
4440
4441
4442
4443/**
4444 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
4445 */
4446static DECLCALLBACK(int) rtFsFatVol_Close(void *pvThis)
4447{
4448 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
4449 LogFlow(("rtFsFatVol_Close(%p)\n", pThis));
4450
4451 int rc = VINF_SUCCESS;
4452 if (pThis->pRootDir != NULL)
4453 {
4454 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
4455 Assert(pThis->pRootDir->Core.cRefs == 1);
4456 rc = rtFsFatDirShrd_Release(pThis->pRootDir);
4457 pThis->pRootDir = NULL;
4458 }
4459
4460 int rc2 = rtFsFatClusterMap_Destroy(pThis);
4461 if (RT_SUCCESS(rc))
4462 rc = rc2;
4463
4464 RTVfsFileRelease(pThis->hVfsBacking);
4465 pThis->hVfsBacking = NIL_RTVFSFILE;
4466
4467 return rc;
4468}
4469
4470
4471/**
4472 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
4473 */
4474static DECLCALLBACK(int) rtFsFatVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4475{
4476 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
4477 return VERR_WRONG_TYPE;
4478}
4479
4480
4481/**
4482 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
4483 */
4484static DECLCALLBACK(int) rtFsFatVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
4485{
4486 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
4487
4488 rtFsFatDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
4489 return rtFsFatDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
4490}
4491
4492
4493/**
4494 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
4495 */
4496static DECLCALLBACK(int) rtFsFatVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
4497{
4498 RT_NOREF(pvThis, off, cb, pfUsed);
4499 return VERR_NOT_IMPLEMENTED;
4500}
4501
4502
4503DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsFatVolOps =
4504{
4505 { /* Obj */
4506 RTVFSOBJOPS_VERSION,
4507 RTVFSOBJTYPE_VFS,
4508 "FatVol",
4509 rtFsFatVol_Close,
4510 rtFsFatVol_QueryInfo,
4511 RTVFSOBJOPS_VERSION
4512 },
4513 RTVFSOPS_VERSION,
4514 0 /* fFeatures */,
4515 rtFsFatVol_OpenRoot,
4516 rtFsFatVol_IsRangeInUse,
4517 RTVFSOPS_VERSION
4518};
4519
4520
4521/**
4522 * Tries to detect a DOS 1.x formatted image and fills in the BPB fields.
4523 *
4524 * There is no BPB here, but fortunately, there isn't much variety.
4525 *
4526 * @returns IPRT status code.
4527 * @param pThis The FAT volume instance, BPB derived fields are filled
4528 * in on success.
4529 * @param pBootSector The boot sector.
4530 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
4531 * the boot sector.
4532 * @param pErrInfo Where to return additional error information.
4533 */
4534static int rtFsFatVolTryInitDos1x(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t const *pbFatSector,
4535 PRTERRINFO pErrInfo)
4536{
4537 /*
4538 * PC-DOS 1.0 does a 2fh byte short jump w/o any NOP following it.
4539 * Instead the following are three words and a 9 byte build date
4540 * string. The remaining space is zero filled.
4541 *
4542 * Note! No idea how this would look like for 8" floppies, only got 5"1/4'.
4543 *
4544 * ASSUME all non-BPB disks are using this format.
4545 */
4546 if ( pBootSector->abJmp[0] != 0xeb /* jmp rel8 */
4547 || pBootSector->abJmp[1] < 0x2f
4548 || pBootSector->abJmp[1] >= 0x80
4549 || pBootSector->abJmp[2] == 0x90 /* nop */)
4550 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4551 "No DOS v1.0 bootsector either - invalid jmp: %.3Rhxs", pBootSector->abJmp);
4552 uint32_t const offJump = 2 + pBootSector->abJmp[1];
4553 uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
4554 Assert(offFirstZero >= RT_UOFFSETOF(FATBOOTSECTOR, Bpb));
4555 uint32_t const cbZeroPad = RT_MIN(offJump - offFirstZero,
4556 sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_OFFSETOF(FATBOOTSECTOR, Bpb)));
4557
4558 if (!ASMMemIsAllU8((uint8_t const *)pBootSector + offFirstZero, cbZeroPad, 0))
4559 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4560 "No DOS v1.0 bootsector either - expected zero padding %#x LB %#x: %.*Rhxs",
4561 offFirstZero, cbZeroPad, cbZeroPad, (uint8_t const *)pBootSector + offFirstZero);
4562
4563 /*
4564 * Check the FAT ID so we can tell if this is double or single sided,
4565 * as well as being a valid FAT12 start.
4566 */
4567 if ( (pbFatSector[0] != 0xfe && pbFatSector[0] != 0xff)
4568 || pbFatSector[1] != 0xff
4569 || pbFatSector[2] != 0xff)
4570 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4571 "No DOS v1.0 bootsector either - unexpected start of FAT: %.3Rhxs", pbFatSector);
4572
4573 /*
4574 * Fixed DOS 1.0 config.
4575 */
4576 pThis->enmFatType = RTFSFATTYPE_FAT12;
4577 pThis->enmBpbVersion = RTFSFATBPBVER_NO_BPB;
4578 pThis->bMedia = pbFatSector[0];
4579 pThis->cReservedSectors = 1;
4580 pThis->cbSector = 512;
4581 pThis->cbCluster = pThis->bMedia == 0xfe ? 1024 : 512;
4582 pThis->cFats = 2;
4583 pThis->cbFat = 512;
4584 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * 512;
4585 pThis->aoffFats[1] = pThis->aoffFats[0] + pThis->cbFat;
4586 pThis->offRootDir = pThis->aoffFats[1] + pThis->cbFat;
4587 pThis->cRootDirEntries = 512;
4588 pThis->offFirstCluster = pThis->offRootDir + RT_ALIGN_32(pThis->cRootDirEntries * sizeof(FATDIRENTRY),
4589 pThis->cbSector);
4590 pThis->cbTotalSize = pThis->bMedia == 0xfe ? 8 * 1 * 40 * 512 : 8 * 2 * 40 * 512;
4591 pThis->cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
4592 return VINF_SUCCESS;
4593}
4594
4595
4596/**
4597 * Worker for rtFsFatVolTryInitDos2Plus that handles remaining BPB fields.
4598 *
4599 * @returns IPRT status code.
4600 * @param pThis The FAT volume instance, BPB derived fields are filled
4601 * in on success.
4602 * @param pBootSector The boot sector.
4603 * @param fMaybe331 Set if it could be a DOS v3.31 BPB.
4604 * @param pErrInfo Where to return additional error information.
4605 */
4606static int rtFsFatVolTryInitDos2PlusBpb(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, bool fMaybe331, PRTERRINFO pErrInfo)
4607{
4608 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_2_0;
4609
4610 /*
4611 * Figure total sector count. Could both be zero, in which case we have to
4612 * fall back on the size of the backing stuff.
4613 */
4614 if (pBootSector->Bpb.Bpb20.cTotalSectors16 != 0)
4615 pThis->cbTotalSize = pBootSector->Bpb.Bpb20.cTotalSectors16 * pThis->cbSector;
4616 else if ( pBootSector->Bpb.Bpb331.cTotalSectors32 != 0
4617 && fMaybe331)
4618 {
4619 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_3_31;
4620 pThis->cbTotalSize = pBootSector->Bpb.Bpb331.cTotalSectors32 * (uint64_t)pThis->cbSector;
4621 }
4622 else
4623 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
4624 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
4625 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4626 "Bogus FAT12/16 total or reserved sector count: %#x vs %#x",
4627 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
4628
4629 /*
4630 * The fat size. Complete FAT offsets.
4631 */
4632 if ( pBootSector->Bpb.Bpb20.cSectorsPerFat == 0
4633 || ((uint32_t)pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cFats + 1) * pThis->cbSector > pThis->cbTotalSize)
4634 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 sectors per FAT: %#x (total sectors %#RX64)",
4635 pBootSector->Bpb.Bpb20.cSectorsPerFat, pThis->cbTotalSize / pThis->cbSector);
4636 pThis->cbFat = pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cbSector;
4637
4638 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
4639 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
4640 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
4641
4642 /*
4643 * Do root directory calculations.
4644 */
4645 pThis->idxRootDirCluster = UINT32_MAX;
4646 pThis->offRootDir = pThis->aoffFats[pThis->cFats];
4647 if (pThis->cRootDirEntries == 0)
4648 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero FAT12/16 root directory size");
4649 pThis->cbRootDir = pThis->cRootDirEntries * sizeof(FATDIRENTRY);
4650 pThis->cbRootDir = RT_ALIGN_32(pThis->cbRootDir, pThis->cbSector);
4651
4652 /*
4653 * First cluster and cluster count checks and calcs. Determin FAT type.
4654 */
4655 pThis->offFirstCluster = pThis->offRootDir + pThis->cbRootDir;
4656 uint64_t cbSystemStuff = pThis->offFirstCluster - pThis->offBootSector;
4657 if (cbSystemStuff >= pThis->cbTotalSize)
4658 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 total size, root dir, or fat size");
4659 pThis->cClusters = (pThis->cbTotalSize - cbSystemStuff) / pThis->cbCluster;
4660
4661 if (pThis->cClusters >= FAT_MAX_FAT16_DATA_CLUSTERS)
4662 {
4663 pThis->cClusters = FAT_MAX_FAT16_DATA_CLUSTERS;
4664 pThis->enmFatType = RTFSFATTYPE_FAT16;
4665 }
4666 else if (pThis->cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS)
4667 pThis->enmFatType = RTFSFATTYPE_FAT16;
4668 else
4669 pThis->enmFatType = RTFSFATTYPE_FAT12; /** @todo Not sure if this is entirely the right way to go about it... */
4670
4671 uint32_t cClustersPerFat;
4672 if (pThis->enmFatType == RTFSFATTYPE_FAT16)
4673 cClustersPerFat = pThis->cbFat / 2;
4674 else
4675 cClustersPerFat = pThis->cbFat * 2 / 3;
4676 if (pThis->cClusters > cClustersPerFat)
4677 pThis->cClusters = cClustersPerFat;
4678
4679 return VINF_SUCCESS;
4680}
4681
4682
4683/**
4684 * Worker for rtFsFatVolTryInitDos2Plus and rtFsFatVolTryInitDos2PlusFat32 that
4685 * handles common extended BPBs fields.
4686 *
4687 * @returns IPRT status code.
4688 * @param pThis The FAT volume instance.
4689 * @param bExtSignature The extended BPB signature.
4690 * @param uSerialNumber The serial number.
4691 * @param pachLabel Pointer to the volume label field.
4692 * @param pachType Pointer to the file system type field.
4693 */
4694static void rtFsFatVolInitCommonEbpbBits(PRTFSFATVOL pThis, uint8_t bExtSignature, uint32_t uSerialNumber,
4695 char const *pachLabel, char const *pachType)
4696{
4697 pThis->uSerialNo = uSerialNumber;
4698 if (bExtSignature == FATEBPB_SIGNATURE)
4699 {
4700 memcpy(pThis->szLabel, pachLabel, RT_SIZEOFMEMB(FATEBPB, achLabel));
4701 pThis->szLabel[RT_SIZEOFMEMB(FATEBPB, achLabel)] = '\0';
4702 RTStrStrip(pThis->szLabel);
4703
4704 memcpy(pThis->szType, pachType, RT_SIZEOFMEMB(FATEBPB, achType));
4705 pThis->szType[RT_SIZEOFMEMB(FATEBPB, achType)] = '\0';
4706 RTStrStrip(pThis->szType);
4707 }
4708 else
4709 {
4710 pThis->szLabel[0] = '\0';
4711 pThis->szType[0] = '\0';
4712 }
4713}
4714
4715
4716/**
4717 * Worker for rtFsFatVolTryInitDos2Plus that deals with FAT32.
4718 *
4719 * @returns IPRT status code.
4720 * @param pThis The FAT volume instance, BPB derived fields are filled
4721 * in on success.
4722 * @param pBootSector The boot sector.
4723 * @param pErrInfo Where to return additional error information.
4724 */
4725static int rtFsFatVolTryInitDos2PlusFat32(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, PRTERRINFO pErrInfo)
4726{
4727 pThis->enmFatType = RTFSFATTYPE_FAT32;
4728 pThis->enmBpbVersion = pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE
4729 ? RTFSFATBPBVER_FAT32_29 : RTFSFATBPBVER_FAT32_28;
4730 pThis->fFat32Flags = pBootSector->Bpb.Fat32Ebpb.fFlags;
4731
4732 if (pBootSector->Bpb.Fat32Ebpb.uVersion != FAT32EBPB_VERSION_0_0)
4733 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Unsupported FAT32 version: %d.%d (%#x)",
4734 RT_HI_U8(pBootSector->Bpb.Fat32Ebpb.uVersion), RT_LO_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),
4735 pBootSector->Bpb.Fat32Ebpb.uVersion);
4736
4737 /*
4738 * Figure total sector count. We expected it to be filled in.
4739 */
4740 bool fUsing64BitTotalSectorCount = false;
4741 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 != 0)
4742 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 * pThis->cbSector;
4743 else if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 != 0)
4744 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 * (uint64_t)pThis->cbSector;
4745 else if ( pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 <= UINT64_MAX / 512
4746 && pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 > 3
4747 && pBootSector->Bpb.Fat32Ebpb.bExtSignature != FATEBPB_SIGNATURE_OLD)
4748 {
4749 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 * pThis->cbSector;
4750 fUsing64BitTotalSectorCount = true;
4751 }
4752 else
4753 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 total sector count out of range: %#RX64",
4754 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64);
4755 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
4756 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4757 "Bogus FAT32 total or reserved sector count: %#x vs %#x",
4758 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
4759
4760 /*
4761 * Fat size. We check the 16-bit field even if it probably should be zero all the time.
4762 */
4763 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat != 0)
4764 {
4765 if ( pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != 0
4766 && pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat)
4767 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4768 "Both 16-bit and 32-bit FAT size fields are set: %#RX16 vs %#RX32",
4769 pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat, pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
4770 pThis->cbFat = pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat * pThis->cbSector;
4771 }
4772 else
4773 {
4774 uint64_t cbFat = pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 * (uint64_t)pThis->cbSector;
4775 if ( cbFat == 0
4776 || cbFat >= FAT_MAX_FAT32_TOTAL_CLUSTERS * 4 + pThis->cbSector * 16)
4777 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4778 "Bogus 32-bit FAT size: %#RX32", pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
4779 pThis->cbFat = (uint32_t)cbFat;
4780 }
4781
4782 /*
4783 * Complete the FAT offsets and first cluster offset, then calculate number
4784 * of data clusters.
4785 */
4786 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
4787 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
4788 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
4789 pThis->offFirstCluster = pThis->aoffFats[pThis->cFats];
4790
4791 if (pThis->offFirstCluster - pThis->offBootSector >= pThis->cbTotalSize)
4792 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4793 "Bogus 32-bit FAT size or total sector count: cFats=%d cbFat=%#x cbTotalSize=%#x",
4794 pThis->cFats, pThis->cbFat, pThis->cbTotalSize);
4795
4796 uint64_t cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
4797 if (cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS)
4798 pThis->cClusters = (uint32_t)cClusters;
4799 else
4800 pThis->cClusters = FAT_MAX_FAT32_DATA_CLUSTERS;
4801 if (pThis->cClusters > (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER))
4802 pThis->cClusters = (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER);
4803
4804 /*
4805 * Root dir cluster.
4806 */
4807 if ( pBootSector->Bpb.Fat32Ebpb.uRootDirCluster < FAT_FIRST_DATA_CLUSTER
4808 || pBootSector->Bpb.Fat32Ebpb.uRootDirCluster >= pThis->cClusters)
4809 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4810 "Bogus FAT32 root directory cluster: %#x", pBootSector->Bpb.Fat32Ebpb.uRootDirCluster);
4811 pThis->idxRootDirCluster = pBootSector->Bpb.Fat32Ebpb.uRootDirCluster;
4812 pThis->offRootDir = pThis->offFirstCluster
4813 + (pBootSector->Bpb.Fat32Ebpb.uRootDirCluster - FAT_FIRST_DATA_CLUSTER) * pThis->cbCluster;
4814
4815 /*
4816 * Info sector.
4817 */
4818 if ( pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == 0
4819 || pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == UINT16_MAX)
4820 pThis->offFat32InfoSector = UINT64_MAX;
4821 else if (pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo >= pThis->cReservedSectors)
4822 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4823 "Bogus FAT32 info sector number: %#x (reserved sectors %#x)",
4824 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pThis->cReservedSectors);
4825 else
4826 {
4827 pThis->offFat32InfoSector = pThis->cbSector * pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo + pThis->offBootSector;
4828 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->offFat32InfoSector,
4829 &pThis->Fat32InfoSector, sizeof(pThis->Fat32InfoSector), NULL);
4830 if (RT_FAILURE(rc))
4831 return RTErrInfoSetF(pErrInfo, rc, "Failed to read FAT32 info sector at offset %#RX64", pThis->offFat32InfoSector);
4832 if ( pThis->Fat32InfoSector.uSignature1 != FAT32INFOSECTOR_SIGNATURE_1
4833 || pThis->Fat32InfoSector.uSignature2 != FAT32INFOSECTOR_SIGNATURE_2
4834 || pThis->Fat32InfoSector.uSignature3 != FAT32INFOSECTOR_SIGNATURE_3)
4835 return RTErrInfoSetF(pErrInfo, rc, "FAT32 info sector signature mismatch: %#x %#x %#x",
4836 pThis->Fat32InfoSector.uSignature1, pThis->Fat32InfoSector.uSignature2,
4837 pThis->Fat32InfoSector.uSignature3);
4838 }
4839
4840 /*
4841 * Boot sector copy.
4842 */
4843 if ( pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == 0
4844 || pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == UINT16_MAX)
4845 {
4846 pThis->cBootSectorCopies = 0;
4847 pThis->offBootSectorCopies = UINT64_MAX;
4848 }
4849 else if (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo >= pThis->cReservedSectors)
4850 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4851 "Bogus FAT32 info boot sector copy location: %#x (reserved sectors %#x)",
4852 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo, pThis->cReservedSectors);
4853 else
4854 {
4855 /** @todo not sure if cbSector is correct here. */
4856 pThis->cBootSectorCopies = 3;
4857 if ( (uint32_t)pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo + pThis->cBootSectorCopies
4858 > pThis->cReservedSectors)
4859 pThis->cBootSectorCopies = (uint8_t)(pThis->cReservedSectors - pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
4860 pThis->offBootSectorCopies = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo * pThis->cbSector + pThis->offBootSector;
4861 if ( pThis->offFat32InfoSector != UINT64_MAX
4862 && pThis->offFat32InfoSector - pThis->offBootSectorCopies < (uint64_t)(pThis->cBootSectorCopies * pThis->cbSector))
4863 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 info sector and boot sector copies overlap: %#x vs %#x",
4864 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
4865 }
4866
4867 /*
4868 * Serial number, label and type.
4869 */
4870 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Fat32Ebpb.bExtSignature, pBootSector->Bpb.Fat32Ebpb.uSerialNumber,
4871 pBootSector->Bpb.Fat32Ebpb.achLabel,
4872 fUsing64BitTotalSectorCount ? pBootSector->achOemName : pBootSector->Bpb.Fat32Ebpb.achLabel);
4873 if (pThis->szType[0] == '\0')
4874 memcpy(pThis->szType, "FAT32", 6);
4875
4876 return VINF_SUCCESS;
4877}
4878
4879
4880/**
4881 * Tries to detect a DOS 2.0+ formatted image and fills in the BPB fields.
4882 *
4883 * We ASSUME BPB here, but need to figure out which version of the BPB it is,
4884 * which is lots of fun.
4885 *
4886 * @returns IPRT status code.
4887 * @param pThis The FAT volume instance, BPB derived fields are filled
4888 * in on success.
4889 * @param pBootSector The boot sector.
4890 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
4891 * the boot sector. On successful return it will contain
4892 * the first FAT sector.
4893 * @param pErrInfo Where to return additional error information.
4894 */
4895static int rtFsFatVolTryInitDos2Plus(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t *pbFatSector, PRTERRINFO pErrInfo)
4896{
4897 /*
4898 * Check if we've got a known jump instruction first, because that will
4899 * give us a max (E)BPB size hint.
4900 */
4901 uint8_t offJmp = UINT8_MAX;
4902 if ( pBootSector->abJmp[0] == 0xeb
4903 && pBootSector->abJmp[1] <= 0x7f)
4904 offJmp = pBootSector->abJmp[1] + 2;
4905 else if ( pBootSector->abJmp[0] == 0x90
4906 && pBootSector->abJmp[1] == 0xeb
4907 && pBootSector->abJmp[2] <= 0x7f)
4908 offJmp = pBootSector->abJmp[2] + 3;
4909 else if ( pBootSector->abJmp[0] == 0xe9
4910 && pBootSector->abJmp[2] <= 0x7f)
4911 offJmp = RT_MIN(127, RT_MAKE_U16(pBootSector->abJmp[1], pBootSector->abJmp[2]));
4912 uint8_t const cbMaxBpb = offJmp - RT_OFFSETOF(FATBOOTSECTOR, Bpb);
4913
4914 /*
4915 * Do the basic DOS v2.0 BPB fields.
4916 */
4917 if (cbMaxBpb < sizeof(FATBPB20))
4918 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4919 "DOS signature, but jmp too short for any BPB: %#x (max %#x BPB)", offJmp, cbMaxBpb);
4920
4921 if (pBootSector->Bpb.Bpb20.cFats == 0)
4922 return RTErrInfoSet(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, number of FATs is zero, so not FAT file system");
4923 if (pBootSector->Bpb.Bpb20.cFats > 4)
4924 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many FATs: %#x", pBootSector->Bpb.Bpb20.cFats);
4925 pThis->cFats = pBootSector->Bpb.Bpb20.cFats;
4926
4927 if (!FATBPB_MEDIA_IS_VALID(pBootSector->Bpb.Bpb20.bMedia))
4928 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4929 "DOS signature, invalid media byte: %#x", pBootSector->Bpb.Bpb20.bMedia);
4930 pThis->bMedia = pBootSector->Bpb.Bpb20.bMedia;
4931
4932 if (!RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cbSector))
4933 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4934 "DOS signature, sector size not power of two: %#x", pBootSector->Bpb.Bpb20.cbSector);
4935 if ( pBootSector->Bpb.Bpb20.cbSector != 512
4936 && pBootSector->Bpb.Bpb20.cbSector != 4096
4937 && pBootSector->Bpb.Bpb20.cbSector != 1024
4938 && pBootSector->Bpb.Bpb20.cbSector != 128)
4939 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4940 "DOS signature, unsupported sector size: %#x", pBootSector->Bpb.Bpb20.cbSector);
4941 pThis->cbSector = pBootSector->Bpb.Bpb20.cbSector;
4942
4943 if ( !RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cSectorsPerCluster)
4944 || !pBootSector->Bpb.Bpb20.cSectorsPerCluster)
4945 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, cluster size not non-zero power of two: %#x",
4946 pBootSector->Bpb.Bpb20.cSectorsPerCluster);
4947 pThis->cbCluster = pBootSector->Bpb.Bpb20.cSectorsPerCluster * pThis->cbSector;
4948
4949 uint64_t const cMaxRoot = (pThis->cbBacking - pThis->offBootSector - 512) / sizeof(FATDIRENTRY); /* we'll check again later. */
4950 if (pBootSector->Bpb.Bpb20.cMaxRootDirEntries >= cMaxRoot)
4951 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many root entries: %#x (max %#RX64)",
4952 pBootSector->Bpb.Bpb20.cSectorsPerCluster, cMaxRoot);
4953 pThis->cRootDirEntries = pBootSector->Bpb.Bpb20.cMaxRootDirEntries;
4954
4955 if ( pBootSector->Bpb.Bpb20.cReservedSectors == 0
4956 || pBootSector->Bpb.Bpb20.cReservedSectors >= _32K)
4957 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4958 "DOS signature, bogus reserved sector count: %#x", pBootSector->Bpb.Bpb20.cReservedSectors);
4959 pThis->cReservedSectors = pBootSector->Bpb.Bpb20.cReservedSectors;
4960 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * pThis->cbSector;
4961
4962 /*
4963 * Jump ahead and check for FAT32 EBPB.
4964 * If found, we simply ASSUME it's a FAT32 file system.
4965 */
4966 int rc;
4967 if ( ( sizeof(FAT32EBPB) <= cbMaxBpb
4968 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE)
4969 || ( RT_OFFSETOF(FAT32EBPB, achLabel) <= cbMaxBpb
4970 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
4971 {
4972 rc = rtFsFatVolTryInitDos2PlusFat32(pThis, pBootSector, pErrInfo);
4973 if (RT_FAILURE(rc))
4974 return rc;
4975 }
4976 else
4977 {
4978 /*
4979 * Check for extended BPB, otherwise we'll have to make qualified guesses
4980 * about what kind of BPB we're up against based on jmp offset and zero fields.
4981 * ASSUMES either FAT16 or FAT12.
4982 */
4983 if ( ( sizeof(FATEBPB) <= cbMaxBpb
4984 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE)
4985 || ( RT_OFFSETOF(FATEBPB, achLabel) <= cbMaxBpb
4986 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
4987 {
4988 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Ebpb.bExtSignature, pBootSector->Bpb.Ebpb.uSerialNumber,
4989 pBootSector->Bpb.Ebpb.achLabel, pBootSector->Bpb.Ebpb.achType);
4990 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, true /*fMaybe331*/, pErrInfo);
4991 pThis->enmBpbVersion = pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE
4992 ? RTFSFATBPBVER_EXT_29 : RTFSFATBPBVER_EXT_28;
4993 }
4994 else
4995 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, cbMaxBpb >= sizeof(FATBPB331), pErrInfo);
4996 if (RT_FAILURE(rc))
4997 return rc;
4998 if (pThis->szType[0] == '\0')
4999 memcpy(pThis->szType, pThis->enmFatType == RTFSFATTYPE_FAT12 ? "FAT12" : "FAT16", 6);
5000 }
5001
5002 /*
5003 * Check the FAT ID. May have to read a bit of the FAT into the buffer.
5004 */
5005 if (pThis->aoffFats[0] != pThis->offBootSector + 512)
5006 {
5007 rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0], pbFatSector, 512, NULL);
5008 if (RT_FAILURE(rc))
5009 return RTErrInfoSet(pErrInfo, rc, "error reading first FAT sector");
5010 }
5011 if (pbFatSector[0] != pThis->bMedia)
5012 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5013 "Media byte and FAT ID mismatch: %#x vs %#x (%.7Rhxs)", pbFatSector[0], pThis->bMedia, pbFatSector);
5014 uint32_t idxOurEndOfChain;
5015 switch (pThis->enmFatType)
5016 {
5017 case RTFSFATTYPE_FAT12:
5018 if ((pbFatSector[1] & 0xf) != 0xf)
5019 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT12): %.3Rhxs", pbFatSector);
5020 pThis->idxMaxLastCluster = FAT_LAST_FAT12_DATA_CLUSTER;
5021 pThis->idxEndOfChain = (pbFatSector[1] >> 4) | ((uint32_t)pbFatSector[2] << 4);
5022 idxOurEndOfChain = FAT_FIRST_FAT12_EOC;
5023 break;
5024
5025 case RTFSFATTYPE_FAT16:
5026 if (pbFatSector[1] != 0xff)
5027 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT16): %.4Rhxs", pbFatSector);
5028 pThis->idxMaxLastCluster = FAT_LAST_FAT16_DATA_CLUSTER;
5029 pThis->idxEndOfChain = RT_MAKE_U16(pbFatSector[2], pbFatSector[3]);
5030 idxOurEndOfChain = FAT_FIRST_FAT16_EOC;
5031 break;
5032
5033 case RTFSFATTYPE_FAT32:
5034 if ( pbFatSector[1] != 0xff
5035 || pbFatSector[2] != 0xff
5036 || (pbFatSector[3] & 0x0f) != 0x0f)
5037 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT32): %.8Rhxs", pbFatSector);
5038 pThis->idxMaxLastCluster = FAT_LAST_FAT32_DATA_CLUSTER;
5039 pThis->idxEndOfChain = RT_MAKE_U32_FROM_U8(pbFatSector[4], pbFatSector[5], pbFatSector[6], pbFatSector[7]);
5040 idxOurEndOfChain = FAT_FIRST_FAT32_EOC;
5041 break;
5042
5043 default: AssertFailedReturn(VERR_INTERNAL_ERROR_2);
5044 }
5045
5046 if (pThis->idxEndOfChain <= pThis->idxMaxLastCluster)
5047 {
5048 Log(("rtFsFatVolTryInitDos2Plus: Bogus idxEndOfChain=%#x, using %#x instead\n", pThis->idxEndOfChain, idxOurEndOfChain));
5049 pThis->idxEndOfChain = idxOurEndOfChain;
5050 }
5051
5052 RT_NOREF(pbFatSector);
5053 return VINF_SUCCESS;
5054}
5055
5056
5057/**
5058 * Given a power of two value @a cb return exponent value.
5059 *
5060 * @returns Shift count
5061 * @param cb The value.
5062 */
5063static uint8_t rtFsFatVolCalcByteShiftCount(uint32_t cb)
5064{
5065 Assert(RT_IS_POWER_OF_TWO(cb));
5066 unsigned iBit = ASMBitFirstSetU32(cb);
5067 Assert(iBit >= 1);
5068 iBit--;
5069 return iBit;
5070}
5071
5072
5073/**
5074 * Worker for RTFsFatVolOpen.
5075 *
5076 * @returns IPRT status code.
5077 * @param pThis The FAT VFS instance to initialize.
5078 * @param hVfsSelf The FAT VFS handle (no reference consumed).
5079 * @param hVfsBacking The file backing the alleged FAT file system.
5080 * Reference is consumed (via rtFsFatVol_Destroy).
5081 * @param fReadOnly Readonly or readwrite mount.
5082 * @param offBootSector The boot sector offset in bytes.
5083 * @param pErrInfo Where to return additional error info. Can be NULL.
5084 */
5085static int rtFsFatVolTryInit(PRTFSFATVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking,
5086 bool fReadOnly, uint64_t offBootSector, PRTERRINFO pErrInfo)
5087{
5088 /*
5089 * First initialize the state so that rtFsFatVol_Destroy won't trip up.
5090 */
5091 pThis->hVfsSelf = hVfsSelf;
5092 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsFatVol_Destroy releases it. */
5093 pThis->cbBacking = 0;
5094 pThis->offBootSector = offBootSector;
5095 pThis->offNanoUTC = RTTimeLocalDeltaNano();
5096 pThis->offMinUTC = pThis->offNanoUTC / RT_NS_1MIN;
5097 pThis->fReadOnly = fReadOnly;
5098 pThis->cReservedSectors = 1;
5099
5100 pThis->cbSector = 512;
5101 pThis->cbCluster = 512;
5102 pThis->cClusters = 0;
5103 pThis->offFirstCluster = 0;
5104 pThis->cbTotalSize = 0;
5105
5106 pThis->enmFatType = RTFSFATTYPE_INVALID;
5107 pThis->cFatEntries = 0;
5108 pThis->cFats = 0;
5109 pThis->cbFat = 0;
5110 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aoffFats); i++)
5111 pThis->aoffFats[i] = UINT64_MAX;
5112 pThis->pFatCache = NULL;
5113
5114 pThis->offRootDir = UINT64_MAX;
5115 pThis->idxRootDirCluster = UINT32_MAX;
5116 pThis->cRootDirEntries = UINT32_MAX;
5117 pThis->cbRootDir = 0;
5118 pThis->pRootDir = NULL;
5119
5120 pThis->uSerialNo = 0;
5121 pThis->szLabel[0] = '\0';
5122 pThis->szType[0] = '\0';
5123 pThis->cBootSectorCopies = 0;
5124 pThis->fFat32Flags = 0;
5125 pThis->offBootSectorCopies = UINT64_MAX;
5126 pThis->offFat32InfoSector = UINT64_MAX;
5127 RT_ZERO(pThis->Fat32InfoSector);
5128
5129 /*
5130 * Get stuff that may fail.
5131 */
5132 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
5133 if (RT_FAILURE(rc))
5134 return rc;
5135 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
5136
5137 /*
5138 * Read the boot sector and the following sector (start of the allocation
5139 * table unless it a FAT32 FS). We'll then validate the boot sector and
5140 * start of the FAT, expanding the BPB into the instance data.
5141 */
5142 union
5143 {
5144 uint8_t ab[512*2];
5145 uint16_t au16[512*2 / 2];
5146 uint32_t au32[512*2 / 4];
5147 FATBOOTSECTOR BootSector;
5148 FAT32INFOSECTOR InfoSector;
5149 } Buf;
5150 RT_ZERO(Buf);
5151
5152 rc = RTVfsFileReadAt(hVfsBacking, offBootSector, &Buf.BootSector, 512 * 2, NULL);
5153 if (RT_FAILURE(rc))
5154 return RTErrInfoSet(pErrInfo, rc, "Unable to read bootsect");
5155
5156 /*
5157 * Extract info from the BPB and validate the two special FAT entries.
5158 *
5159 * Check the DOS signature first. The PC-DOS 1.0 boot floppy does not have
5160 * a signature and we ASSUME this is the case for all flopies formated by it.
5161 */
5162 if (Buf.BootSector.uSignature != FATBOOTSECTOR_SIGNATURE)
5163 {
5164 if (Buf.BootSector.uSignature != 0)
5165 return RTErrInfoSetF(pErrInfo, rc, "No DOS bootsector signature: %#06x", Buf.BootSector.uSignature);
5166 rc = rtFsFatVolTryInitDos1x(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
5167 }
5168 else
5169 rc = rtFsFatVolTryInitDos2Plus(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
5170 if (RT_FAILURE(rc))
5171 return rc;
5172
5173 /*
5174 * Calc shift counts.
5175 */
5176 pThis->cSectorByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbSector);
5177 pThis->cClusterByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbCluster);
5178
5179 /*
5180 * Setup the FAT cache.
5181 */
5182 rc = rtFsFatClusterMap_Create(pThis, &Buf.ab[512], pErrInfo);
5183 if (RT_FAILURE(rc))
5184 return rc;
5185
5186 /*
5187 * Create the root directory fun.
5188 */
5189 if (pThis->idxRootDirCluster == UINT32_MAX)
5190 rc = rtFsFatDirShrd_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
5191 UINT32_MAX, pThis->offRootDir, pThis->cbRootDir, &pThis->pRootDir);
5192 else
5193 rc = rtFsFatDirShrd_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
5194 pThis->idxRootDirCluster, UINT64_MAX, pThis->cbRootDir, &pThis->pRootDir);
5195 return rc;
5196}
5197
5198
5199/**
5200 * Opens a FAT file system volume.
5201 *
5202 * @returns IPRT status code.
5203 * @param hVfsFileIn The file or device backing the volume.
5204 * @param fReadOnly Whether to mount it read-only.
5205 * @param offBootSector The offset of the boot sector relative to the start
5206 * of @a hVfsFileIn. Pass 0 for floppies.
5207 * @param phVfs Where to return the virtual file system handle.
5208 * @param pErrInfo Where to return additional error information.
5209 */
5210RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, uint64_t offBootSector, PRTVFS phVfs, PRTERRINFO pErrInfo)
5211{
5212 /*
5213 * Quick input validation.
5214 */
5215 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
5216 *phVfs = NIL_RTVFS;
5217
5218 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
5219 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5220
5221 /*
5222 * Create a new FAT VFS instance and try initialize it using the given input file.
5223 */
5224 RTVFS hVfs = NIL_RTVFS;
5225 void *pvThis = NULL;
5226 int rc = RTVfsNew(&g_rtFsFatVolOps, sizeof(RTFSFATVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
5227 if (RT_SUCCESS(rc))
5228 {
5229 rc = rtFsFatVolTryInit((PRTFSFATVOL)pvThis, hVfs, hVfsFileIn, fReadOnly, offBootSector, pErrInfo);
5230 if (RT_SUCCESS(rc))
5231 *phVfs = hVfs;
5232 else
5233 RTVfsRelease(hVfs);
5234 }
5235 else
5236 RTVfsFileRelease(hVfsFileIn);
5237 return rc;
5238}
5239
5240
5241
5242
5243/**
5244 * Fills a range in the file with zeros in the most efficient manner.
5245 *
5246 * @returns IPRT status code.
5247 * @param hVfsFile The file to write to.
5248 * @param off Where to start filling with zeros.
5249 * @param cbZeros How many zero blocks to write.
5250 */
5251static int rtFsFatVolWriteZeros(RTVFSFILE hVfsFile, uint64_t off, uint32_t cbZeros)
5252{
5253 while (cbZeros > 0)
5254 {
5255 uint32_t cbToWrite = sizeof(g_abRTZero64K);
5256 if (cbToWrite > cbZeros)
5257 cbToWrite = cbZeros;
5258 int rc = RTVfsFileWriteAt(hVfsFile, off, g_abRTZero64K, cbToWrite, NULL);
5259 if (RT_FAILURE(rc))
5260 return rc;
5261 off += cbToWrite;
5262 cbZeros -= cbToWrite;
5263 }
5264 return VINF_SUCCESS;
5265}
5266
5267
5268/**
5269 * Formats a FAT volume.
5270 *
5271 * @returns IRPT status code.
5272 * @param hVfsFile The volume file.
5273 * @param offVol The offset into @a hVfsFile of the file.
5274 * Typically 0.
5275 * @param cbVol The size of the volume. Pass 0 if the rest of
5276 * hVfsFile should be used.
5277 * @param fFlags See RTFSFATVOL_FMT_F_XXX.
5278 * @param cbSector The logical sector size. Must be power of two.
5279 * Optional, pass zero to use 512.
5280 * @param cSectorsPerCluster Number of sectors per cluster. Power of two.
5281 * Optional, pass zero to auto detect.
5282 * @param enmFatType The FAT type (12, 16, 32) to use.
5283 * Optional, pass RTFSFATTYPE_INVALID for default.
5284 * @param cHeads The number of heads to report in the BPB.
5285 * Optional, pass zero to auto detect.
5286 * @param cSectorsPerTrack The number of sectors per track to put in the
5287 * BPB. Optional, pass zero to auto detect.
5288 * @param bMedia The media byte value and FAT ID to use.
5289 * Optional, pass zero to auto detect.
5290 * @param cRootDirEntries Number of root directory entries.
5291 * Optional, pass zero to auto detect.
5292 * @param cHiddenSectors Number of hidden sectors. Pass 0 for
5293 * unpartitioned media.
5294 * @param pErrInfo Additional error information, maybe. Optional.
5295 */
5296RTDECL(int) RTFsFatVolFormat(RTVFSFILE hVfsFile, uint64_t offVol, uint64_t cbVol, uint32_t fFlags, uint16_t cbSector,
5297 uint16_t cSectorsPerCluster, RTFSFATTYPE enmFatType, uint32_t cHeads, uint32_t cSectorsPerTrack,
5298 uint8_t bMedia, uint16_t cRootDirEntries, uint32_t cHiddenSectors, PRTERRINFO pErrInfo)
5299{
5300 int rc;
5301 uint32_t cFats = 2;
5302
5303 /*
5304 * Validate input.
5305 */
5306 if (!cbSector)
5307 cbSector = 512;
5308 else
5309 AssertMsgReturn( cbSector == 128
5310 || cbSector == 512
5311 || cbSector == 1024
5312 || cbSector == 4096,
5313 ("cbSector=%#x\n", cbSector),
5314 VERR_INVALID_PARAMETER);
5315 AssertMsgReturn(cSectorsPerCluster == 0 || (cSectorsPerCluster <= 128 && RT_IS_POWER_OF_TWO(cSectorsPerCluster)),
5316 ("cSectorsPerCluster=%#x\n", cSectorsPerCluster), VERR_INVALID_PARAMETER);
5317 if (bMedia != 0)
5318 {
5319 AssertMsgReturn(FAT_ID_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
5320 AssertMsgReturn(FATBPB_MEDIA_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
5321 }
5322 AssertReturn(!(fFlags & ~RTFSFATVOL_FMT_F_VALID_MASK), VERR_INVALID_FLAGS);
5323 AssertReturn(enmFatType >= RTFSFATTYPE_INVALID && enmFatType < RTFSFATTYPE_END, VERR_INVALID_PARAMETER);
5324
5325 if (!cbVol)
5326 {
5327 uint64_t cbFile;
5328 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
5329 AssertRCReturn(rc, rc);
5330 AssertMsgReturn(cbFile > offVol, ("cbFile=%#RX64 offVol=%#RX64\n", cbFile, offVol), VERR_INVALID_PARAMETER);
5331 cbVol = cbFile - offVol;
5332 }
5333 uint64_t const cSectorsInVol = cbVol / cbSector;
5334
5335 /*
5336 * Guess defaults if necessary.
5337 */
5338 if (!cSectorsPerCluster || !cHeads || !cSectorsPerTrack || !bMedia || !cRootDirEntries)
5339 {
5340 static struct
5341 {
5342 uint64_t cbVol;
5343 uint8_t bMedia;
5344 uint8_t cHeads;
5345 uint8_t cSectorsPerTrack;
5346 uint8_t cSectorsPerCluster;
5347 uint16_t cRootDirEntries;
5348 } s_aDefaults[] =
5349 {
5350 /* cbVol, bMedia, cHeads, cSectorsPTrk, cSectorsPClstr, cRootDirEntries */
5351 { 163840, 0xfe, /* cyl: 40,*/ 1, 8, 1, 64 },
5352 { 184320, 0xfc, /* cyl: 40,*/ 1, 9, 2, 64 },
5353 { 327680, 0xff, /* cyl: 40,*/ 2, 8, 2, 112 },
5354 { 368640, 0xfd, /* cyl: 40,*/ 2, 9, 2, 112 },
5355 { 737280, 0xf9, /* cyl: 80,*/ 2, 9, 2, 112 },
5356 { 1228800, 0xf9, /* cyl: 80,*/ 2, 15, 2, 112 },
5357 { 1474560, 0xf0, /* cyl: 80,*/ 2, 18, 1, 224 },
5358 { 2949120, 0xf0, /* cyl: 80,*/ 2, 36, 2, 224 },
5359 { 528482304, 0xf8, /* cyl: 1024,*/ 16, 63, 0, 512 }, // 504MB limit
5360 { UINT64_C(7927234560), 0xf8, /* cyl: 1024,*/ 240, 63, 0, 512 }, // 7.3GB limit
5361 { UINT64_C(8422686720), 0xf8, /* cyl: 1024,*/ 255, 63, 0, 512 }, // 7.84GB limit
5362
5363 };
5364 uint32_t iDefault = 0;
5365 while ( iDefault < RT_ELEMENTS(s_aDefaults) - 1U
5366 && cbVol > s_aDefaults[iDefault].cbVol)
5367 iDefault++;
5368 if (!cHeads)
5369 cHeads = s_aDefaults[iDefault].cHeads;
5370 if (!cSectorsPerTrack)
5371 cSectorsPerTrack = s_aDefaults[iDefault].cSectorsPerTrack;
5372 if (!bMedia)
5373 bMedia = s_aDefaults[iDefault].bMedia;
5374 if (!cRootDirEntries)
5375 cRootDirEntries = s_aDefaults[iDefault].cRootDirEntries;
5376 if (!cSectorsPerCluster)
5377 {
5378 cSectorsPerCluster = s_aDefaults[iDefault].cSectorsPerCluster;
5379 if (!cSectorsPerCluster)
5380 {
5381 uint32_t cbFat12Overhead = cbSector /* boot sector */
5382 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
5383 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
5384 uint32_t cbFat16Overhead = cbSector /* boot sector */
5385 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
5386 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
5387
5388 if ( enmFatType == RTFSFATTYPE_FAT12
5389 || cbVol <= cbFat12Overhead + FAT_MAX_FAT12_DATA_CLUSTERS * 4 * cbSector)
5390 {
5391 enmFatType = RTFSFATTYPE_FAT12;
5392 cSectorsPerCluster = 1;
5393 while ( cSectorsPerCluster < 128
5394 && cSectorsInVol
5395 > cbFat12Overhead / cbSector
5396 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT12_DATA_CLUSTERS
5397 + cSectorsPerCluster - 1)
5398 cSectorsPerCluster <<= 1;
5399 }
5400 else if ( enmFatType == RTFSFATTYPE_FAT16
5401 || cbVol <= cbFat16Overhead + FAT_MAX_FAT16_DATA_CLUSTERS * 128 * cbSector)
5402 {
5403 enmFatType = RTFSFATTYPE_FAT16;
5404 cSectorsPerCluster = 1;
5405 while ( cSectorsPerCluster < 128
5406 && cSectorsInVol
5407 > cbFat12Overhead / cbSector
5408 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT16_DATA_CLUSTERS
5409 + cSectorsPerCluster - 1)
5410 cSectorsPerCluster <<= 1;
5411 }
5412 else
5413 {
5414 /* The target here is keeping the FAT size below 8MB. Seems windows
5415 likes a minimum 4KB cluster size as wells as a max of 32KB (googling). */
5416 enmFatType = RTFSFATTYPE_FAT32;
5417 uint32_t cbFat32Overhead = cbSector * 32 /* boot sector, info sector, boot sector copies, reserved sectors */
5418 + _8M * cFats;
5419 if (cbSector >= _4K)
5420 cSectorsPerCluster = 1;
5421 else
5422 cSectorsPerCluster = _4K / cbSector;
5423 while ( cSectorsPerCluster < 128
5424 && cSectorsPerCluster * cbSector < _32K
5425 && cSectorsInVol > cbFat32Overhead / cbSector + (uint64_t)cSectorsPerCluster * _2M)
5426 cSectorsPerCluster <<= 1;
5427 }
5428 }
5429 }
5430 }
5431 Assert(cSectorsPerCluster);
5432 Assert(cRootDirEntries);
5433 uint32_t cbRootDir = RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector);
5434 uint32_t const cbCluster = cSectorsPerCluster * cbSector;
5435
5436 /*
5437 * If we haven't figured out the FAT type yet, do so.
5438 * The file system code determins the FAT based on cluster counts,
5439 * so we must do so here too.
5440 */
5441 if (enmFatType == RTFSFATTYPE_INVALID)
5442 {
5443 uint32_t cbFat12Overhead = cbSector /* boot sector */
5444 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
5445 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
5446 if ( cbVol <= cbFat12Overhead + cbCluster
5447 || (cbVol - cbFat12Overhead) / cbCluster <= FAT_MAX_FAT12_DATA_CLUSTERS)
5448 enmFatType = RTFSFATTYPE_FAT12;
5449 else
5450 {
5451 uint32_t cbFat16Overhead = cbSector /* boot sector */
5452 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
5453 + cbRootDir;
5454 if ( cbVol <= cbFat16Overhead + cbCluster
5455 || (cbVol - cbFat16Overhead) / cbCluster <= FAT_MAX_FAT16_DATA_CLUSTERS)
5456 enmFatType = RTFSFATTYPE_FAT16;
5457 else
5458 enmFatType = RTFSFATTYPE_FAT32;
5459 }
5460 }
5461 if (enmFatType == RTFSFATTYPE_FAT32)
5462 cbRootDir = cbCluster;
5463
5464 /*
5465 * Calculate the FAT size and number of data cluster.
5466 *
5467 * Since the FAT size depends on how many data clusters there are, we start
5468 * with a minimum FAT size and maximum clust count, then recalucate it. The
5469 * result isn't necessarily stable, so we will only retry stabalizing the
5470 * result a few times.
5471 */
5472 uint32_t cbReservedFixed = enmFatType == RTFSFATTYPE_FAT32 ? 32 * cbSector : cbSector + cbRootDir;
5473 uint32_t cbFat = cbSector;
5474 if (cbReservedFixed + cbFat * cFats >= cbVol)
5475 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
5476 cbVol, cbReservedFixed, cbFat, cFats);
5477 uint32_t cMaxClusters = enmFatType == RTFSFATTYPE_FAT12 ? FAT_MAX_FAT12_DATA_CLUSTERS
5478 : enmFatType == RTFSFATTYPE_FAT16 ? FAT_MAX_FAT16_DATA_CLUSTERS
5479 : FAT_MAX_FAT12_DATA_CLUSTERS;
5480 uint32_t cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
5481 uint32_t cPrevClusters;
5482 uint32_t cTries = 4;
5483 do
5484 {
5485 cPrevClusters = cClusters;
5486 switch (enmFatType)
5487 {
5488 case RTFSFATTYPE_FAT12:
5489 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT12_TOTAL_CLUSTERS, cClusters) * 3 / 2;
5490 break;
5491 case RTFSFATTYPE_FAT16:
5492 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT16_TOTAL_CLUSTERS, cClusters) * 2;
5493 break;
5494 case RTFSFATTYPE_FAT32:
5495 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT32_TOTAL_CLUSTERS, cClusters) * 4;
5496 cbFat = RT_ALIGN_32(cbFat, _4K);
5497 break;
5498 default:
5499 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
5500 }
5501 cbFat = RT_ALIGN_32(cbFat, cbSector);
5502 if (cbReservedFixed + cbFat * cFats >= cbVol)
5503 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
5504 cbVol, cbReservedFixed, cbFat, cFats);
5505 cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
5506 } while ( cClusters != cPrevClusters
5507 && cTries-- > 0);
5508 uint64_t const cTotalSectors = cClusters * (uint64_t)cSectorsPerCluster + (cbReservedFixed + cbFat * cFats) / cbSector;
5509
5510 /*
5511 * Check that the file system type and cluster count matches up. If they
5512 * don't the type will be misdetected.
5513 *
5514 * Note! These assertions could trigger if the above calculations are wrong.
5515 */
5516 switch (enmFatType)
5517 {
5518 case RTFSFATTYPE_FAT12:
5519 AssertMsgReturn(cClusters >= FAT_MIN_FAT12_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT12_DATA_CLUSTERS,
5520 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
5521 break;
5522 case RTFSFATTYPE_FAT16:
5523 AssertMsgReturn(cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT16_DATA_CLUSTERS,
5524 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
5525 break;
5526 case RTFSFATTYPE_FAT32:
5527 AssertMsgReturn(cClusters >= FAT_MIN_FAT32_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS,
5528 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
5529 RT_FALL_THRU();
5530 default:
5531 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
5532 }
5533
5534 /*
5535 * Okay, create the boot sector.
5536 */
5537 size_t cbBuf = RT_MAX(RT_MAX(_64K, cbCluster), cbSector * 2U);
5538 uint8_t *pbBuf = (uint8_t *)RTMemTmpAllocZ(cbBuf);
5539 AssertReturn(pbBuf, VERR_NO_TMP_MEMORY);
5540
5541 const char *pszLastOp = "boot sector";
5542 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pbBuf;
5543 pBootSector->abJmp[0] = 0xeb;
5544 pBootSector->abJmp[1] = RT_UOFFSETOF(FATBOOTSECTOR, Bpb)
5545 + (enmFatType == RTFSFATTYPE_FAT32 ? sizeof(FAT32EBPB) : sizeof(FATEBPB)) - 2;
5546 pBootSector->abJmp[2] = 0x90;
5547 memcpy(pBootSector->achOemName, enmFatType == RTFSFATTYPE_FAT32 ? "FAT32 " : "IPRT 6.2", sizeof(pBootSector->achOemName));
5548 pBootSector->Bpb.Bpb331.cbSector = (uint16_t)cbSector;
5549 pBootSector->Bpb.Bpb331.cSectorsPerCluster = (uint8_t)cSectorsPerCluster;
5550 pBootSector->Bpb.Bpb331.cReservedSectors = enmFatType == RTFSFATTYPE_FAT32 ? cbReservedFixed / cbSector : 1;
5551 pBootSector->Bpb.Bpb331.cFats = (uint8_t)cFats;
5552 pBootSector->Bpb.Bpb331.cMaxRootDirEntries = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cRootDirEntries;
5553 pBootSector->Bpb.Bpb331.cTotalSectors16 = cTotalSectors <= UINT16_MAX ? (uint16_t)cTotalSectors : 0;
5554 pBootSector->Bpb.Bpb331.bMedia = bMedia;
5555 pBootSector->Bpb.Bpb331.cSectorsPerFat = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cbFat / cbSector;
5556 pBootSector->Bpb.Bpb331.cSectorsPerTrack = cSectorsPerTrack;
5557 pBootSector->Bpb.Bpb331.cTracksPerCylinder = cHeads;
5558 pBootSector->Bpb.Bpb331.cHiddenSectors = cHiddenSectors;
5559 /* XP barfs if both cTotalSectors32 and cTotalSectors16 are set */
5560 pBootSector->Bpb.Bpb331.cTotalSectors32 = cTotalSectors <= UINT32_MAX && pBootSector->Bpb.Bpb331.cTotalSectors16 == 0
5561 ? (uint32_t)cTotalSectors : 0;
5562 if (enmFatType != RTFSFATTYPE_FAT32)
5563 {
5564 pBootSector->Bpb.Ebpb.bInt13Drive = 0;
5565 pBootSector->Bpb.Ebpb.bReserved = 0;
5566 pBootSector->Bpb.Ebpb.bExtSignature = FATEBPB_SIGNATURE;
5567 pBootSector->Bpb.Ebpb.uSerialNumber = RTRandU32();
5568 memset(pBootSector->Bpb.Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Ebpb.achLabel));
5569 memcpy(pBootSector->Bpb.Ebpb.achType, enmFatType == RTFSFATTYPE_FAT12 ? "FAT12 " : "FAT16 ",
5570 sizeof(pBootSector->Bpb.Ebpb.achType));
5571 }
5572 else
5573 {
5574 pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 = cbFat / cbSector;
5575 pBootSector->Bpb.Fat32Ebpb.fFlags = 0;
5576 pBootSector->Bpb.Fat32Ebpb.uVersion = FAT32EBPB_VERSION_0_0;
5577 pBootSector->Bpb.Fat32Ebpb.uRootDirCluster = FAT_FIRST_DATA_CLUSTER;
5578 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo = 1;
5579 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo = 6;
5580 RT_ZERO(pBootSector->Bpb.Fat32Ebpb.abReserved);
5581
5582 pBootSector->Bpb.Fat32Ebpb.bInt13Drive = 0;
5583 pBootSector->Bpb.Fat32Ebpb.bReserved = 0;
5584 pBootSector->Bpb.Fat32Ebpb.bExtSignature = FATEBPB_SIGNATURE;
5585 pBootSector->Bpb.Fat32Ebpb.uSerialNumber = RTRandU32();
5586 memset(pBootSector->Bpb.Fat32Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Fat32Ebpb.achLabel));
5587 if (cTotalSectors > UINT32_MAX)
5588 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 = cTotalSectors;
5589 else
5590 memcpy(pBootSector->Bpb.Fat32Ebpb.u.achType, "FAT32 ", sizeof(pBootSector->Bpb.Fat32Ebpb.u.achType));
5591 }
5592 pbBuf[pBootSector->abJmp[1] + 2 + 0] = 0xcd; /* int 19h */
5593 pbBuf[pBootSector->abJmp[1] + 2 + 1] = 0x19;
5594 pbBuf[pBootSector->abJmp[1] + 2 + 2] = 0xcc; /* int3 */
5595 pbBuf[pBootSector->abJmp[1] + 2 + 3] = 0xcc;
5596
5597 pBootSector->uSignature = FATBOOTSECTOR_SIGNATURE;
5598 if (cbSector != sizeof(*pBootSector))
5599 *(uint16_t *)&pbBuf[cbSector - 2] = FATBOOTSECTOR_SIGNATURE; /** @todo figure out how disks with non-512 byte sectors work! */
5600
5601 rc = RTVfsFileWriteAt(hVfsFile, offVol, pBootSector, cbSector, NULL);
5602 uint32_t const offFirstFat = pBootSector->Bpb.Bpb331.cReservedSectors * cbSector;
5603
5604 /*
5605 * Write the FAT32 info sector, 3 boot sector copies, and zero fill
5606 * the other reserved sectors.
5607 */
5608 if (RT_SUCCESS(rc) && enmFatType == RTFSFATTYPE_FAT32)
5609 {
5610 pszLastOp = "fat32 info sector";
5611 PFAT32INFOSECTOR pInfoSector = (PFAT32INFOSECTOR)&pbBuf[cbSector]; /* preserve the boot sector. */
5612 RT_ZERO(*pInfoSector);
5613 pInfoSector->uSignature1 = FAT32INFOSECTOR_SIGNATURE_1;
5614 pInfoSector->uSignature2 = FAT32INFOSECTOR_SIGNATURE_2;
5615 pInfoSector->uSignature3 = FAT32INFOSECTOR_SIGNATURE_3;
5616 pInfoSector->cFreeClusters = cClusters - 1; /* ASSUMES 1 cluster for the root dir. */
5617 pInfoSector->cLastAllocatedCluster = FAT_FIRST_DATA_CLUSTER;
5618 rc = RTVfsFileWriteAt(hVfsFile, offVol + cbSector, pInfoSector, cbSector, NULL);
5619
5620 uint32_t iSector = 2;
5621 if (RT_SUCCESS(rc))
5622 {
5623 pszLastOp = "fat32 unused reserved sectors";
5624 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
5625 (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo - iSector) * cbSector);
5626 iSector = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo;
5627 }
5628
5629 if (RT_SUCCESS(rc))
5630 {
5631 pszLastOp = "boot sector copy";
5632 for (uint32_t i = 0; i < 3 && RT_SUCCESS(rc); i++, iSector++)
5633 rc = RTVfsFileWriteAt(hVfsFile, offVol + iSector * cbSector, pBootSector, cbSector, NULL);
5634 }
5635
5636 if (RT_SUCCESS(rc))
5637 {
5638 pszLastOp = "fat32 unused reserved sectors";
5639 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
5640 (pBootSector->Bpb.Bpb331.cReservedSectors - iSector) * cbSector);
5641 }
5642 }
5643
5644 /*
5645 * The FATs.
5646 */
5647 if (RT_SUCCESS(rc))
5648 {
5649 pszLastOp = "fat";
5650 pBootSector = NULL; /* invalid */
5651 RT_BZERO(pbBuf, cbSector);
5652 switch (enmFatType)
5653 {
5654 case RTFSFATTYPE_FAT32:
5655 pbBuf[11] = 0x0f; /* EOC for root dir*/
5656 pbBuf[10] = 0xff;
5657 pbBuf[9] = 0xff;
5658 pbBuf[8] = 0xff;
5659 pbBuf[7] = 0x0f; /* Formatter's EOC, followed by signed extend FAT ID. */
5660 pbBuf[6] = 0xff;
5661 pbBuf[5] = 0xff;
5662 pbBuf[4] = 0xff;
5663 RT_FALL_THRU();
5664 case RTFSFATTYPE_FAT16:
5665 pbBuf[3] = 0xff;
5666 RT_FALL_THRU();
5667 case RTFSFATTYPE_FAT12:
5668 pbBuf[2] = 0xff;
5669 pbBuf[1] = 0xff;
5670 pbBuf[0] = bMedia; /* FAT ID */
5671 break;
5672 default: AssertFailed();
5673 }
5674 for (uint32_t iFatCopy = 0; iFatCopy < cFats && RT_SUCCESS(rc); iFatCopy++)
5675 {
5676 rc = RTVfsFileWriteAt(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy, pbBuf, cbSector, NULL);
5677 if (RT_SUCCESS(rc) && cbFat > cbSector)
5678 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy + cbSector, cbFat - cbSector);
5679 }
5680 }
5681
5682 /*
5683 * The root directory.
5684 */
5685 if (RT_SUCCESS(rc))
5686 {
5687 /** @todo any mandatory directory entries we need to fill in here? */
5688 pszLastOp = "root dir";
5689 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * cFats, cbRootDir);
5690 }
5691
5692 /*
5693 * If long format, fill the rest of the disk with 0xf6.
5694 */
5695 AssertCompile(RTFSFATVOL_FMT_F_QUICK != 0);
5696 if (RT_SUCCESS(rc) && !(fFlags & RTFSFATVOL_FMT_F_QUICK))
5697 {
5698 pszLastOp = "formatting data clusters";
5699 uint64_t offCur = offFirstFat + cbFat * cFats + cbRootDir;
5700 uint64_t cbLeft = cTotalSectors * cbSector;
5701 if (cbVol - cbLeft <= _256K) /* HACK ALERT! Format to end of volume if it's a cluster rounding thing. */
5702 cbLeft = cbVol;
5703 if (cbLeft > offCur)
5704 {
5705 cbLeft -= offCur;
5706 offCur += offVol;
5707
5708 memset(pbBuf, 0xf6, cbBuf);
5709 while (cbLeft > 0)
5710 {
5711 size_t cbToWrite = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
5712 rc = RTVfsFileWriteAt(hVfsFile, offCur, pbBuf, cbToWrite, NULL);
5713 if (RT_SUCCESS(rc))
5714 {
5715 offCur += cbToWrite;
5716 cbLeft -= cbToWrite;
5717 }
5718 else
5719 break;
5720 }
5721 }
5722 }
5723
5724 /*
5725 * Done.
5726 */
5727 RTMemTmpFree(pbBuf);
5728 if (RT_SUCCESS(rc))
5729 return rc;
5730 return RTErrInfoSet(pErrInfo, rc, pszLastOp);
5731}
5732
5733
5734/**
5735 * Formats a 1.44MB floppy image.
5736 *
5737 * @returns IPRT status code.
5738 * @param hVfsFile The image.
5739 */
5740RTDECL(int) RTFsFatVolFormat144(RTVFSFILE hVfsFile, bool fQuick)
5741{
5742 return RTFsFatVolFormat(hVfsFile, 0 /*offVol*/, 1474560, fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
5743 512 /*cbSector*/, 1 /*cSectorsPerCluster*/, RTFSFATTYPE_FAT12, 2 /*cHeads*/, 18 /*cSectors*/,
5744 0xf0 /*bMedia*/, 224 /*cRootDirEntries*/, 0 /*cHiddenSectors*/, NULL /*pErrInfo*/);
5745}
5746
5747
5748/**
5749 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
5750 */
5751static DECLCALLBACK(int) rtVfsChainFatVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
5752 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
5753{
5754 RT_NOREF(pProviderReg);
5755
5756 /*
5757 * Basic checks.
5758 */
5759 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
5760 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
5761 if ( pElement->enmType != RTVFSOBJTYPE_VFS
5762 && pElement->enmType != RTVFSOBJTYPE_DIR)
5763 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
5764 if (pElement->cArgs > 1)
5765 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
5766
5767 /*
5768 * Parse the flag if present, save in pElement->uProvider.
5769 */
5770 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
5771 if (pElement->cArgs > 0)
5772 {
5773 const char *psz = pElement->paArgs[0].psz;
5774 if (*psz)
5775 {
5776 if (!strcmp(psz, "ro"))
5777 fReadOnly = true;
5778 else if (!strcmp(psz, "rw"))
5779 fReadOnly = false;
5780 else
5781 {
5782 *poffError = pElement->paArgs[0].offSpec;
5783 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
5784 }
5785 }
5786 }
5787
5788 pElement->uProvider = fReadOnly;
5789 return VINF_SUCCESS;
5790}
5791
5792
5793/**
5794 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
5795 */
5796static DECLCALLBACK(int) rtVfsChainFatVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
5797 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
5798 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
5799{
5800 RT_NOREF(pProviderReg, pSpec, poffError);
5801
5802 int rc;
5803 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
5804 if (hVfsFileIn != NIL_RTVFSFILE)
5805 {
5806 RTVFS hVfs;
5807 rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, 0, &hVfs, pErrInfo);
5808 RTVfsFileRelease(hVfsFileIn);
5809 if (RT_SUCCESS(rc))
5810 {
5811 *phVfsObj = RTVfsObjFromVfs(hVfs);
5812 RTVfsRelease(hVfs);
5813 if (*phVfsObj != NIL_RTVFSOBJ)
5814 return VINF_SUCCESS;
5815 rc = VERR_VFS_CHAIN_CAST_FAILED;
5816 }
5817 }
5818 else
5819 rc = VERR_VFS_CHAIN_CAST_FAILED;
5820 return rc;
5821}
5822
5823
5824/**
5825 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
5826 */
5827static DECLCALLBACK(bool) rtVfsChainFatVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
5828 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
5829 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
5830{
5831 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
5832 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
5833 || !pReuseElement->paArgs[0].uProvider)
5834 return true;
5835 return false;
5836}
5837
5838
5839/** VFS chain element 'file'. */
5840static RTVFSCHAINELEMENTREG g_rtVfsChainFatVolReg =
5841{
5842 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
5843 /* fReserved = */ 0,
5844 /* pszName = */ "fat",
5845 /* ListEntry = */ { NULL, NULL },
5846 /* pszHelp = */ "Open a FAT file system, requires a file object on the left side.\n"
5847 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
5848 /* pfnValidate = */ rtVfsChainFatVol_Validate,
5849 /* pfnInstantiate = */ rtVfsChainFatVol_Instantiate,
5850 /* pfnCanReuseElement = */ rtVfsChainFatVol_CanReuseElement,
5851 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
5852};
5853
5854RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainFatVolReg, rtVfsChainFatVolReg);
5855
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