VirtualBox

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

Last change on this file since 91538 was 84509, checked in by vboxsync, 5 years ago

iprt/cdefs.h,*: Introducing RT_FLEXIBLE_ARRAY_EXTENSION as a g++ hack that allows us to use RT_FLEXIBLE_ARRAY without the compiler going all pendantic on us. Only tested with 10.1.0. bugref:9746

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