VirtualBox

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

最後變更 在這個檔案從94272是 93115,由 vboxsync 提交於 3 年 前

scm --update-copyright-year

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

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