VirtualBox

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

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

fatvfs: don't match or return volume lables

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