VirtualBox

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

Last change on this file since 72526 was 69977, checked in by vboxsync, 7 years ago

IPRT/vfs: Implemented RTVFsFileSetSize, RTVfsFileGetMaxSize and RTvfsFileQueryMaxSize.

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