VirtualBox

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

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

IPRT/fatvfs.cpp: Fixed bug in rtFsFatChain_GetClusterByIndex that caused issues accessing large files and replacing them.

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