VirtualBox

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

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

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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