VirtualBox

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

Last change on this file since 76408 was 76408, checked in by vboxsync, 6 years ago

iprt/string.h: Dropped including utf16.h and let those who need it include it themselves. bugref:9344

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

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