VirtualBox

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

Last change on this file since 100908 was 100908, checked in by vboxsync, 17 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

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

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