VirtualBox

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

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

IPRT: VFS IsRangeInUse cleanup.

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

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