VirtualBox

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

Last change on this file since 95032 was 94291, checked in by vboxsync, 3 years ago

IPRT,Storage: Adding RTVfsQueryLabel and internally a generic pfnQueryInfoEx method to the RTVFSOBJOPS function table. Untested implementation of the latter for iso/udf. bugref:9781

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

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