VirtualBox

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

Last change on this file was 103108, checked in by vboxsync, 4 months ago

IPRT/fatvfs.cpp: Simplied r166121/r158845.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use