VirtualBox

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

Last change on this file since 69705 was 69678, checked in by vboxsync, 8 years ago

fatvfs.cpp: forgot to disable electric fence

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