VirtualBox

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

Last change on this file since 96923 was 96923, checked in by vboxsync, 2 years ago

IPRT/fatvfs.cpp: Do int18h instead of 19h in the FAT non-boot code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 238.6 KB
Line 
1/* $Id: fatvfs.cpp 96923 2022-09-28 20:33:21Z vboxsync $ */
2/** @file
3 * IPRT - FAT Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2017-2022 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, PCRTSGBUF 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 return rc;
2233}
2234
2235
2236/**
2237 * Changes the size of a file or directory FAT object.
2238 *
2239 * @returns IPRT status code
2240 * @param pObj The common object.
2241 * @param cbFile The new file size.
2242 */
2243static int rtFsFatObj_SetSize(PRTFSFATOBJ pObj, uint32_t cbFile)
2244{
2245 AssertReturn( ((pObj->cbObject + pObj->Clusters.cbCluster - 1) >> pObj->Clusters.cClusterByteShift)
2246 == pObj->Clusters.cClusters, VERR_INTERNAL_ERROR_3);
2247
2248 /*
2249 * Do nothing if the size didn't change.
2250 */
2251 if (pObj->cbObject == cbFile)
2252 return VINF_SUCCESS;
2253
2254 /*
2255 * Do we need to allocate or free clusters?
2256 */
2257 int rc = VINF_SUCCESS;
2258 uint32_t const cClustersNew = (cbFile + pObj->Clusters.cbCluster - 1) >> pObj->Clusters.cClusterByteShift;
2259 AssertReturn(pObj->pParentDir, VERR_INTERNAL_ERROR_2);
2260 if (pObj->Clusters.cClusters == cClustersNew)
2261 { /* likely when writing small bits at a time. */ }
2262 else if (pObj->Clusters.cClusters < cClustersNew)
2263 {
2264 /* Allocate and append new clusters. */
2265 do
2266 {
2267 uint32_t idxCluster;
2268 rc = rtFsFatClusterMap_AllocateCluster(pObj->pVol, rtFsFatChain_GetLastCluster(&pObj->Clusters), &idxCluster);
2269 if (RT_SUCCESS(rc))
2270 rc = rtFsFatChain_Append(&pObj->Clusters, idxCluster);
2271 } while (pObj->Clusters.cClusters < cClustersNew && RT_SUCCESS(rc));
2272 pObj->fMaybeDirtyFat = true;
2273 }
2274 else
2275 {
2276 /* Free clusters we don't need any more. */
2277 if (cClustersNew > 0)
2278 rc = rtFsFatClusterMap_SetEndOfChain(pObj->pVol, rtFsFatChain_GetClusterByIndex(&pObj->Clusters, cClustersNew - 1));
2279 if (RT_SUCCESS(rc))
2280 {
2281 uint32_t iClusterToFree = cClustersNew;
2282 while (iClusterToFree < pObj->Clusters.cClusters && RT_SUCCESS(rc))
2283 {
2284 rc = rtFsFatClusterMap_FreeCluster(pObj->pVol, rtFsFatChain_GetClusterByIndex(&pObj->Clusters, iClusterToFree));
2285 iClusterToFree++;
2286 }
2287
2288 rtFsFatChain_Shrink(&pObj->Clusters, cClustersNew);
2289 }
2290 pObj->fMaybeDirtyFat = true;
2291 }
2292 if (RT_SUCCESS(rc))
2293 {
2294 /*
2295 * Update the object size, since we've got the right number of clusters backing it now.
2296 */
2297 pObj->cbObject = cbFile;
2298
2299 /*
2300 * Update the directory entry.
2301 */
2302 uint32_t uWriteLock;
2303 PFATDIRENTRY pDirEntry;
2304 rc = rtFsFatDirShrd_GetEntryForUpdate(pObj->pParentDir, pObj->offEntryInDir, &pDirEntry, &uWriteLock);
2305 if (RT_SUCCESS(rc))
2306 {
2307 pDirEntry->cbFile = cbFile;
2308 uint32_t idxFirstCluster;
2309 if (cClustersNew == 0)
2310 idxFirstCluster = 0; /** @todo figure out if setting the cluster to 0 is the right way to deal with empty files... */
2311 else
2312 idxFirstCluster = rtFsFatChain_GetFirstCluster(&pObj->Clusters);
2313 pDirEntry->idxCluster = (uint16_t)idxFirstCluster;
2314 if (pObj->pVol->enmFatType >= RTFSFATTYPE_FAT32)
2315 pDirEntry->u.idxClusterHigh = (uint16_t)(idxFirstCluster >> 16);
2316
2317 rc = rtFsFatDirShrd_PutEntryAfterUpdate(pObj->pParentDir, pDirEntry, uWriteLock);
2318 pObj->fMaybeDirtyDirEnt = true;
2319 }
2320 }
2321 Log3(("rtFsFatObj_SetSize: Returns %Rrc\n", rc));
2322 return rc;
2323}
2324
2325
2326/**
2327 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
2328 */
2329static DECLCALLBACK(int) rtFsFatFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
2330{
2331 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2332 PRTFSFATFILESHRD pShared = pThis->pShared;
2333 PRTFSFATVOL pVol = pShared->Core.pVol;
2334 AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
2335 RT_NOREF(fBlocking);
2336
2337 if (pVol->fReadOnly)
2338 return VERR_WRITE_PROTECT;
2339
2340 if (off == -1)
2341 off = pThis->offFile;
2342
2343 /*
2344 * Do the reading cluster by cluster.
2345 */
2346 int rc = VINF_SUCCESS;
2347 uint32_t cbWritten = 0;
2348 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
2349 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
2350 while (cbLeft > 0)
2351 {
2352 /* Figure out how much we can write. Checking for max file size and such. */
2353 uint32_t cbToWrite = pShared->Core.Clusters.cbCluster - ((uint32_t)off & (pShared->Core.Clusters.cbCluster - 1));
2354 if (cbToWrite > cbLeft)
2355 cbToWrite = (uint32_t)cbLeft;
2356 uint64_t offNew = (uint64_t)off + cbToWrite;
2357 if (offNew < _4G)
2358 { /*likely*/ }
2359 else if ((uint64_t)off < _4G - 1U)
2360 cbToWrite = _4G - 1U - off;
2361 else
2362 {
2363 rc = VERR_FILE_TOO_BIG;
2364 break;
2365 }
2366
2367 /* Grow the file? */
2368 if ((uint32_t)offNew > pShared->Core.cbObject)
2369 {
2370 rc = rtFsFatObj_SetSize(&pShared->Core, (uint32_t)offNew);
2371 if (RT_SUCCESS(rc))
2372 { /* likely */}
2373 else
2374 break;
2375 }
2376
2377 /* Figure the disk offset. */
2378 uint64_t offDisk = rtFsFatChain_FileOffsetToDiskOff(&pShared->Core.Clusters, (uint32_t)off, pVol);
2379 if (offDisk != UINT64_MAX)
2380 {
2381 rc = RTVfsFileWriteAt(pVol->hVfsBacking, offDisk, pbSrc, cbToWrite, NULL);
2382 if (RT_SUCCESS(rc))
2383 {
2384 off += cbToWrite;
2385 pbSrc += cbToWrite;
2386 cbWritten += cbToWrite;
2387 cbLeft -= cbToWrite;
2388 }
2389 else
2390 break;
2391 }
2392 else
2393 {
2394 rc = VERR_VFS_BOGUS_OFFSET;
2395 break;
2396 }
2397 }
2398
2399 /* Update the offset and return. */
2400 pThis->offFile = off;
2401 if (pcbWritten)
2402 *pcbWritten = cbWritten;
2403 return rc;
2404}
2405
2406
2407/**
2408 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
2409 */
2410static DECLCALLBACK(int) rtFsFatFile_Flush(void *pvThis)
2411{
2412 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2413 PRTFSFATFILESHRD pShared = pThis->pShared;
2414 int rc1 = rtFsFatObj_FlushMetaData(&pShared->Core);
2415 int rc2 = RTVfsFileFlush(pShared->Core.pVol->hVfsBacking);
2416 return RT_FAILURE(rc1) ? rc1 : rc2;
2417}
2418
2419
2420/**
2421 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
2422 */
2423static DECLCALLBACK(int) rtFsFatFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
2424 uint32_t *pfRetEvents)
2425{
2426 NOREF(pvThis);
2427 int rc;
2428 if (fEvents != RTPOLL_EVT_ERROR)
2429 {
2430 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
2431 rc = VINF_SUCCESS;
2432 }
2433 else if (fIntr)
2434 rc = RTThreadSleep(cMillies);
2435 else
2436 {
2437 uint64_t uMsStart = RTTimeMilliTS();
2438 do
2439 rc = RTThreadSleep(cMillies);
2440 while ( rc == VERR_INTERRUPTED
2441 && !fIntr
2442 && RTTimeMilliTS() - uMsStart < cMillies);
2443 if (rc == VERR_INTERRUPTED)
2444 rc = VERR_TIMEOUT;
2445 }
2446 return rc;
2447}
2448
2449
2450/**
2451 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
2452 */
2453static DECLCALLBACK(int) rtFsFatFile_Tell(void *pvThis, PRTFOFF poffActual)
2454{
2455 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2456 *poffActual = pThis->offFile;
2457 return VINF_SUCCESS;
2458}
2459
2460
2461/**
2462 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2463 */
2464static DECLCALLBACK(int) rtFsFatFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2465{
2466 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2467 return rtFsFatObj_SetMode(&pThis->pShared->Core, fMode, fMask);
2468}
2469
2470
2471/**
2472 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2473 */
2474static DECLCALLBACK(int) rtFsFatFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2475 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2476{
2477 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2478 return rtFsFatObj_SetTimes(&pThis->pShared->Core, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2479}
2480
2481
2482/**
2483 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2484 */
2485static DECLCALLBACK(int) rtFsFatFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2486{
2487 RT_NOREF(pvThis, uid, gid);
2488 return VERR_NOT_SUPPORTED;
2489}
2490
2491
2492/**
2493 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
2494 */
2495static DECLCALLBACK(int) rtFsFatFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
2496{
2497 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2498 PRTFSFATFILESHRD pShared = pThis->pShared;
2499
2500 RTFOFF offNew;
2501 switch (uMethod)
2502 {
2503 case RTFILE_SEEK_BEGIN:
2504 offNew = offSeek;
2505 break;
2506 case RTFILE_SEEK_END:
2507 offNew = (RTFOFF)pShared->Core.cbObject + offSeek;
2508 break;
2509 case RTFILE_SEEK_CURRENT:
2510 offNew = (RTFOFF)pThis->offFile + offSeek;
2511 break;
2512 default:
2513 return VERR_INVALID_PARAMETER;
2514 }
2515 if (offNew >= 0)
2516 {
2517 if (offNew <= _4G)
2518 {
2519 pThis->offFile = offNew;
2520 *poffActual = offNew;
2521 return VINF_SUCCESS;
2522 }
2523 return VERR_OUT_OF_RANGE;
2524 }
2525 return VERR_NEGATIVE_SEEK;
2526}
2527
2528
2529/**
2530 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
2531 */
2532static DECLCALLBACK(int) rtFsFatFile_QuerySize(void *pvThis, uint64_t *pcbFile)
2533{
2534 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2535 PRTFSFATFILESHRD pShared = pThis->pShared;
2536 *pcbFile = pShared->Core.cbObject;
2537 return VINF_SUCCESS;
2538}
2539
2540
2541/**
2542 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
2543 */
2544static DECLCALLBACK(int) rtFsFatFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
2545{
2546 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
2547 PRTFSFATFILESHRD pShared = pThis->pShared;
2548 AssertReturn(!fFlags, VERR_NOT_SUPPORTED);
2549 if (cbFile > UINT32_MAX)
2550 return VERR_FILE_TOO_BIG;
2551 return rtFsFatObj_SetSize(&pShared->Core, (uint32_t)cbFile);
2552}
2553
2554
2555/**
2556 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
2557 */
2558static DECLCALLBACK(int) rtFsFatFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
2559{
2560 RT_NOREF(pvThis);
2561 *pcbMax = UINT32_MAX;
2562 return VINF_SUCCESS;
2563}
2564
2565
2566/**
2567 * FAT file operations.
2568 */
2569DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsFatFileOps =
2570{
2571 { /* Stream */
2572 { /* Obj */
2573 RTVFSOBJOPS_VERSION,
2574 RTVFSOBJTYPE_FILE,
2575 "FatFile",
2576 rtFsFatFile_Close,
2577 rtFsFatFile_QueryInfo,
2578 NULL,
2579 RTVFSOBJOPS_VERSION
2580 },
2581 RTVFSIOSTREAMOPS_VERSION,
2582 RTVFSIOSTREAMOPS_FEAT_NO_SG,
2583 rtFsFatFile_Read,
2584 rtFsFatFile_Write,
2585 rtFsFatFile_Flush,
2586 rtFsFatFile_PollOne,
2587 rtFsFatFile_Tell,
2588 NULL /*pfnSkip*/,
2589 NULL /*pfnZeroFill*/,
2590 RTVFSIOSTREAMOPS_VERSION,
2591 },
2592 RTVFSFILEOPS_VERSION,
2593 0,
2594 { /* ObjSet */
2595 RTVFSOBJSETOPS_VERSION,
2596 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
2597 rtFsFatFile_SetMode,
2598 rtFsFatFile_SetTimes,
2599 rtFsFatFile_SetOwner,
2600 RTVFSOBJSETOPS_VERSION
2601 },
2602 rtFsFatFile_Seek,
2603 rtFsFatFile_QuerySize,
2604 rtFsFatFile_SetSize,
2605 rtFsFatFile_QueryMaxSize,
2606 RTVFSFILEOPS_VERSION
2607};
2608
2609
2610/**
2611 * Instantiates a new file.
2612 *
2613 * @returns IPRT status code.
2614 * @param pThis The FAT volume instance.
2615 * @param pParentDir The parent directory.
2616 * @param pDirEntry The parent directory entry.
2617 * @param offEntryInDir The byte offset of the directory entry in the parent
2618 * directory.
2619 * @param fOpen RTFILE_O_XXX flags.
2620 * @param phVfsFile Where to return the file handle.
2621 */
2622static int rtFsFatFile_New(PRTFSFATVOL pThis, PRTFSFATDIRSHRD pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
2623 uint64_t fOpen, PRTVFSFILE phVfsFile)
2624{
2625 AssertPtr(pParentDir);
2626 Assert(!(offEntryInDir & (sizeof(FATDIRENTRY) - 1)));
2627
2628 PRTFSFATFILE pNewFile;
2629 int rc = RTVfsNewFile(&g_rtFsFatFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2630 phVfsFile, (void **)&pNewFile);
2631 if (RT_SUCCESS(rc))
2632 {
2633 pNewFile->offFile = 0;
2634 pNewFile->pShared = NULL;
2635
2636 /*
2637 * Look for existing shared object, create a new one if necessary.
2638 */
2639 PRTFSFATFILESHRD pShared = (PRTFSFATFILESHRD)rtFsFatDirShrd_LookupShared(pParentDir, offEntryInDir);
2640 if (pShared)
2641 {
2642 LogFlow(("rtFsFatFile_New: cbObject=%#RX32 \n", pShared->Core.cbObject));
2643 pNewFile->pShared = pShared;
2644 return VINF_SUCCESS;
2645 }
2646
2647 pShared = (PRTFSFATFILESHRD)RTMemAllocZ(sizeof(*pShared));
2648 if (pShared)
2649 {
2650 rtFsFatObj_InitFromDirEntry(&pShared->Core, pDirEntry, offEntryInDir, pThis);
2651 pNewFile->pShared = pShared;
2652
2653 rc = rtFsFatClusterMap_ReadClusterChain(pThis, RTFSFAT_GET_CLUSTER(pDirEntry, pThis), &pShared->Core.Clusters);
2654 if (RT_SUCCESS(rc))
2655 {
2656 /*
2657 * Link into parent directory so we can use it to update
2658 * our directory entry.
2659 */
2660 rtFsFatDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2661
2662 /*
2663 * Should we truncate the file or anything of that sort?
2664 */
2665 if ( (fOpen & RTFILE_O_TRUNCATE)
2666 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
2667 {
2668 Log3(("rtFsFatFile_New: calling rtFsFatObj_SetSize to zap the file size.\n"));
2669 rc = rtFsFatObj_SetSize(&pShared->Core, 0);
2670 }
2671 if (RT_SUCCESS(rc))
2672 {
2673 LogFlow(("rtFsFatFile_New: cbObject=%#RX32 pShared=%p\n", pShared->Core.cbObject, pShared));
2674 return VINF_SUCCESS;
2675 }
2676 }
2677 }
2678 else
2679 rc = VERR_NO_MEMORY;
2680
2681 /* Destroy the file object. */
2682 RTVfsFileRelease(*phVfsFile);
2683 }
2684 *phVfsFile = NIL_RTVFSFILE;
2685 return rc;
2686}
2687
2688
2689/**
2690 * Looks up the shared structure for a child.
2691 *
2692 * @returns Referenced pointer to the shared structure, NULL if not found.
2693 * @param pThis The directory.
2694 * @param offEntryInDir The directory record offset of the child.
2695 */
2696static PRTFSFATOBJ rtFsFatDirShrd_LookupShared(PRTFSFATDIRSHRD pThis, uint32_t offEntryInDir)
2697{
2698 PRTFSFATOBJ pCur;
2699 RTListForEach(&pThis->OpenChildren, pCur, RTFSFATOBJ, Entry)
2700 {
2701 if (pCur->offEntryInDir == offEntryInDir)
2702 {
2703 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
2704 Assert(cRefs > 1); RT_NOREF(cRefs);
2705 return pCur;
2706 }
2707 }
2708 return NULL;
2709}
2710
2711
2712/**
2713 * Flush directory changes when having a fully buffered directory.
2714 *
2715 * @returns IPRT status code
2716 * @param pThis The directory.
2717 */
2718static int rtFsFatDirShrd_FlushFullyBuffered(PRTFSFATDIRSHRD pThis)
2719{
2720 Assert(pThis->fFullyBuffered);
2721 uint32_t const cbSector = pThis->Core.pVol->cbSector;
2722 RTVFSFILE const hVfsBacking = pThis->Core.pVol->hVfsBacking;
2723 int rc = VINF_SUCCESS;
2724 for (uint32_t i = 0; i < pThis->u.Full.cSectors; i++)
2725 if (ASMBitTest(pThis->u.Full.pbDirtySectors, i))
2726 {
2727 int rc2 = RTVfsFileWriteAt(hVfsBacking, pThis->offEntriesOnDisk + i * cbSector,
2728 (uint8_t *)pThis->paEntries + i * cbSector, cbSector, NULL);
2729 if (RT_SUCCESS(rc2))
2730 ASMBitClear(pThis->u.Full.pbDirtySectors, i);
2731 else if (RT_SUCCESS(rc))
2732 rc = rc2;
2733 }
2734 return rc;
2735}
2736
2737
2738/**
2739 * Flush directory changes when using simple buffering.
2740 *
2741 * @returns IPRT status code
2742 * @param pThis The directory.
2743 */
2744static int rtFsFatDirShrd_FlushSimple(PRTFSFATDIRSHRD pThis)
2745{
2746 Assert(!pThis->fFullyBuffered);
2747 int rc;
2748 if ( !pThis->u.Simple.fDirty
2749 || pThis->offEntriesOnDisk != UINT64_MAX)
2750 rc = VINF_SUCCESS;
2751 else
2752 {
2753 Assert(pThis->u.Simple.offInDir != UINT32_MAX);
2754 rc = RTVfsFileWriteAt(pThis->Core.pVol->hVfsBacking, pThis->offEntriesOnDisk,
2755 pThis->paEntries, pThis->Core.pVol->cbSector, NULL);
2756 if (RT_SUCCESS(rc))
2757 pThis->u.Simple.fDirty = false;
2758 }
2759 return rc;
2760}
2761
2762
2763/**
2764 * Flush directory changes.
2765 *
2766 * @returns IPRT status code
2767 * @param pThis The directory.
2768 */
2769static int rtFsFatDirShrd_Flush(PRTFSFATDIRSHRD pThis)
2770{
2771 if (pThis->fFullyBuffered)
2772 return rtFsFatDirShrd_FlushFullyBuffered(pThis);
2773 return rtFsFatDirShrd_FlushSimple(pThis);
2774}
2775
2776
2777/**
2778 * Gets one or more entires at @a offEntryInDir.
2779 *
2780 * Common worker for rtFsFatDirShrd_GetEntriesAt and rtFsFatDirShrd_GetEntryForUpdate
2781 *
2782 * @returns IPRT status code.
2783 * @param pThis The directory.
2784 * @param offEntryInDir The directory offset in bytes.
2785 * @param fForUpdate Whether it's for updating.
2786 * @param ppaEntries Where to return pointer to the entry at
2787 * @a offEntryInDir.
2788 * @param pcEntries Where to return the number of entries
2789 * @a *ppaEntries points to.
2790 * @param puBufferReadLock Where to return the buffer read lock handle.
2791 * Call rtFsFatDirShrd_ReleaseBufferAfterReading when
2792 * done.
2793 */
2794static int rtFsFatDirShrd_GetEntriesAtCommon(PRTFSFATDIRSHRD pThis, uint32_t offEntryInDir, bool fForUpdate,
2795 PFATDIRENTRYUNION *ppaEntries, uint32_t *pcEntries, uint32_t *puLock)
2796{
2797 *puLock = UINT32_MAX;
2798
2799 int rc;
2800 Assert(RT_ALIGN_32(offEntryInDir, sizeof(FATDIRENTRY)) == offEntryInDir);
2801 Assert(pThis->Core.cbObject / sizeof(FATDIRENTRY) == pThis->cEntries);
2802 uint32_t const idxEntryInDir = offEntryInDir / sizeof(FATDIRENTRY);
2803 if (idxEntryInDir < pThis->cEntries)
2804 {
2805 if (pThis->fFullyBuffered)
2806 {
2807 /*
2808 * Fully buffered: Return pointer to all the entires starting at offEntryInDir.
2809 */
2810 *ppaEntries = &pThis->paEntries[idxEntryInDir];
2811 *pcEntries = pThis->cEntries - idxEntryInDir;
2812 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001);
2813 rc = VINF_SUCCESS;
2814 }
2815 else
2816 {
2817 /*
2818 * Simple buffering: If hit, return the number of entries.
2819 */
2820 PRTFSFATVOL pVol = pThis->Core.pVol;
2821 uint32_t off = offEntryInDir - pThis->u.Simple.offInDir;
2822 if (off < pVol->cbSector)
2823 {
2824 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)];
2825 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY);
2826 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001);
2827 rc = VINF_SUCCESS;
2828 }
2829 else
2830 {
2831 /*
2832 * Simple buffering: Miss.
2833 * Flush dirty. Read in new sector. Return entries in sector starting
2834 * at offEntryInDir.
2835 */
2836 if (!pThis->u.Simple.fDirty)
2837 rc = VINF_SUCCESS;
2838 else
2839 rc = rtFsFatDirShrd_FlushSimple(pThis);
2840 if (RT_SUCCESS(rc))
2841 {
2842 off = offEntryInDir & (pVol->cbSector - 1);
2843 pThis->u.Simple.offInDir = (offEntryInDir & ~(pVol->cbSector - 1));
2844 pThis->offEntriesOnDisk = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, pThis->u.Simple.offInDir,
2845 pThis->Core.pVol);
2846 rc = RTVfsFileReadAt(pThis->Core.pVol->hVfsBacking, pThis->offEntriesOnDisk,
2847 pThis->paEntries, pVol->cbSector, NULL);
2848 if (RT_SUCCESS(rc))
2849 {
2850 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)];
2851 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY);
2852 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001);
2853 rc = VINF_SUCCESS;
2854 }
2855 else
2856 {
2857 pThis->u.Simple.offInDir = UINT32_MAX;
2858 pThis->offEntriesOnDisk = UINT64_MAX;
2859 }
2860 }
2861 }
2862 }
2863 }
2864 else
2865 rc = VERR_FILE_NOT_FOUND;
2866 return rc;
2867}
2868
2869
2870/**
2871 * Puts back a directory entry after updating it, releasing the write lock and
2872 * marking it dirty.
2873 *
2874 * @returns IPRT status code
2875 * @param pThis The directory.
2876 * @param pDirEntry The directory entry.
2877 * @param uWriteLock The write lock.
2878 */
2879static int rtFsFatDirShrd_PutEntryAfterUpdate(PRTFSFATDIRSHRD pThis, PFATDIRENTRY pDirEntry, uint32_t uWriteLock)
2880{
2881 Assert(uWriteLock == UINT32_C(0x80000001));
2882 RT_NOREF(uWriteLock);
2883 if (pThis->fFullyBuffered)
2884 {
2885 uint32_t idxSector = ((uintptr_t)pDirEntry - (uintptr_t)pThis->paEntries) / pThis->Core.pVol->cbSector;
2886 ASMBitSet(pThis->u.Full.pbDirtySectors, idxSector);
2887 }
2888 else
2889 pThis->u.Simple.fDirty = true;
2890 return VINF_SUCCESS;
2891}
2892
2893
2894/**
2895 * Gets the pointer to the given directory entry for the purpose of updating it.
2896 *
2897 * Call rtFsFatDirShrd_PutEntryAfterUpdate afterwards.
2898 *
2899 * @returns IPRT status code.
2900 * @param pThis The directory.
2901 * @param offEntryInDir The byte offset of the directory entry, within the
2902 * directory.
2903 * @param ppDirEntry Where to return the pointer to the directory entry.
2904 * @param puWriteLock Where to return the write lock.
2905 */
2906static int rtFsFatDirShrd_GetEntryForUpdate(PRTFSFATDIRSHRD pThis, uint32_t offEntryInDir, PFATDIRENTRY *ppDirEntry,
2907 uint32_t *puWriteLock)
2908{
2909 uint32_t cEntriesIgn;
2910 return rtFsFatDirShrd_GetEntriesAtCommon(pThis, offEntryInDir, true /*fForUpdate*/, (PFATDIRENTRYUNION *)ppDirEntry,
2911 &cEntriesIgn, puWriteLock);
2912}
2913
2914
2915/**
2916 * Release a directory buffer after done reading from it.
2917 *
2918 * This is currently just a placeholder.
2919 *
2920 * @param pThis The directory.
2921 * @param uBufferReadLock The buffer lock.
2922 */
2923static void rtFsFatDirShrd_ReleaseBufferAfterReading(PRTFSFATDIRSHRD pThis, uint32_t uBufferReadLock)
2924{
2925 RT_NOREF(pThis, uBufferReadLock);
2926 Assert(uBufferReadLock == 1);
2927}
2928
2929
2930/**
2931 * Gets one or more entires at @a offEntryInDir.
2932 *
2933 * @returns IPRT status code.
2934 * @param pThis The directory.
2935 * @param offEntryInDir The directory offset in bytes.
2936 * @param ppaEntries Where to return pointer to the entry at
2937 * @a offEntryInDir.
2938 * @param pcEntries Where to return the number of entries
2939 * @a *ppaEntries points to.
2940 * @param puBufferReadLock Where to return the buffer read lock handle.
2941 * Call rtFsFatDirShrd_ReleaseBufferAfterReading when
2942 * done.
2943 */
2944static int rtFsFatDirShrd_GetEntriesAt(PRTFSFATDIRSHRD pThis, uint32_t offEntryInDir,
2945 PCFATDIRENTRYUNION *ppaEntries, uint32_t *pcEntries, uint32_t *puBufferReadLock)
2946{
2947 return rtFsFatDirShrd_GetEntriesAtCommon(pThis, offEntryInDir, false /*fForUpdate*/, (PFATDIRENTRYUNION *)ppaEntries,
2948 pcEntries, puBufferReadLock);
2949}
2950
2951
2952/**
2953 * Translates a unicode codepoint to an uppercased CP437 index.
2954 *
2955 * @returns CP437 index if valie, UINT16_MAX if not.
2956 * @param uc The codepoint to convert.
2957 */
2958static uint16_t rtFsFatUnicodeCodepointToUpperCodepage(RTUNICP uc)
2959{
2960 /*
2961 * The first 128 chars have 1:1 translation for valid FAT chars.
2962 */
2963 if (uc < 128)
2964 {
2965 if (g_awchFatCp437ValidChars[uc] == uc)
2966 return (uint16_t)uc;
2967 if (RT_C_IS_LOWER(uc))
2968 return uc - 0x20;
2969 return UINT16_MAX;
2970 }
2971
2972 /*
2973 * Try for uppercased, settle for lower case if no upper case variant in the table.
2974 * This is really expensive, btw.
2975 */
2976 RTUNICP ucUpper = RTUniCpToUpper(uc);
2977 for (unsigned i = 128; i < 256; i++)
2978 if (g_awchFatCp437ValidChars[i] == ucUpper)
2979 return i;
2980 if (ucUpper != uc)
2981 for (unsigned i = 128; i < 256; i++)
2982 if (g_awchFatCp437ValidChars[i] == uc)
2983 return i;
2984 return UINT16_MAX;
2985}
2986
2987
2988/**
2989 * Convert filename string to 8-dot-3 format, doing necessary ASCII uppercasing
2990 * and such.
2991 *
2992 * @returns true if 8.3 formattable name, false if not.
2993 * @param pszName8Dot3 Where to return the 8-dot-3 name when returning
2994 * @c true. Filled with zero on false. 8+3+1 bytes.
2995 * @param pszName The filename to convert.
2996 */
2997static bool rtFsFatDir_StringTo8Dot3(char *pszName8Dot3, const char *pszName)
2998{
2999 /*
3000 * Don't try convert names with more than 12 unicode chars in them.
3001 */
3002 size_t const cucName = RTStrUniLen(pszName);
3003 if (cucName <= 12 && cucName > 0)
3004 {
3005 /*
3006 * Recode the input string as CP437, uppercasing it, validating the
3007 * name, formatting it as a FAT directory entry string.
3008 */
3009 size_t offDst = 0;
3010 bool fExt = false;
3011 for (;;)
3012 {
3013 RTUNICP uc;
3014 int rc = RTStrGetCpEx(&pszName, &uc);
3015 if (RT_SUCCESS(rc))
3016 {
3017 if (uc)
3018 {
3019 if (offDst < 8+3)
3020 {
3021 uint16_t idxCp = rtFsFatUnicodeCodepointToUpperCodepage(uc);
3022 if (idxCp != UINT16_MAX)
3023 {
3024 pszName8Dot3[offDst++] = (char)idxCp;
3025 Assert(uc != '.');
3026 continue;
3027 }
3028
3029 /* Maybe the dot? */
3030 if ( uc == '.'
3031 && !fExt
3032 && offDst <= 8)
3033 {
3034 fExt = true;
3035 while (offDst < 8)
3036 pszName8Dot3[offDst++] = ' ';
3037 continue;
3038 }
3039 }
3040 }
3041 /* String terminator: Check length, pad and convert 0xe5. */
3042 else if (offDst <= (size_t)(fExt ? 8 + 3 : 8))
3043 {
3044 while (offDst < 8 + 3)
3045 pszName8Dot3[offDst++] = ' ';
3046 Assert(offDst == 8 + 3);
3047 pszName8Dot3[offDst] = '\0';
3048
3049 if ((uint8_t)pszName8Dot3[0] == FATDIRENTRY_CH0_DELETED)
3050 pszName8Dot3[0] = FATDIRENTRY_CH0_ESC_E5;
3051 return true;
3052 }
3053 }
3054 /* invalid */
3055 break;
3056 }
3057 }
3058 memset(&pszName8Dot3[0], 0, 8+3+1);
3059 return false;
3060}
3061
3062
3063/**
3064 * Calculates the checksum of a directory entry.
3065 * @returns Checksum.
3066 * @param pDirEntry The directory entry to checksum.
3067 */
3068static uint8_t rtFsFatDir_CalcChecksum(PCFATDIRENTRY pDirEntry)
3069{
3070 uint8_t bChecksum = pDirEntry->achName[0];
3071 for (uint8_t off = 1; off < RT_ELEMENTS(pDirEntry->achName); off++)
3072 {
3073 bChecksum = RTFSFAT_ROT_R1_U8(bChecksum);
3074 bChecksum += pDirEntry->achName[off];
3075 }
3076 return bChecksum;
3077}
3078
3079
3080/**
3081 * Locates a directory entry in a directory.
3082 *
3083 * @returns IPRT status code.
3084 * @retval VERR_FILE_NOT_FOUND if not found.
3085 * @param pThis The directory to search.
3086 * @param pszEntry The entry to look for.
3087 * @param poffEntryInDir Where to return the offset of the directory
3088 * entry.
3089 * @param pfLong Where to return long name indicator.
3090 * @param pDirEntry Where to return a copy of the directory entry.
3091 */
3092static int rtFsFatDirShrd_FindEntry(PRTFSFATDIRSHRD pThis, const char *pszEntry, uint32_t *poffEntryInDir, bool *pfLong,
3093 PFATDIRENTRY pDirEntry)
3094{
3095 /* Set return values. */
3096 *pfLong = false;
3097 *poffEntryInDir = UINT32_MAX;
3098
3099 /*
3100 * Turn pszEntry into a 8.3 filename, if possible.
3101 */
3102 char szName8Dot3[8+3+1];
3103 bool fIs8Dot3Name = rtFsFatDir_StringTo8Dot3(szName8Dot3, pszEntry);
3104
3105 /*
3106 * Scan the directory buffer by buffer.
3107 */
3108 RTUTF16 wszName[260+1];
3109 uint8_t bChecksum = UINT8_MAX;
3110 uint8_t idNextSlot = UINT8_MAX;
3111 size_t cwcName = 0;
3112 uint32_t offEntryInDir = 0;
3113 uint32_t const cbDir = pThis->Core.cbObject;
3114 Assert(RT_ALIGN_32(cbDir, sizeof(*pDirEntry)) == cbDir);
3115 AssertCompile(FATDIRNAMESLOT_MAX_SLOTS * FATDIRNAMESLOT_CHARS_PER_SLOT < RT_ELEMENTS(wszName));
3116 wszName[260] = '\0';
3117
3118 while (offEntryInDir < cbDir)
3119 {
3120 /* Get chunk of entries starting at offEntryInDir. */
3121 uint32_t uBufferLock = UINT32_MAX;
3122 uint32_t cEntries = 0;
3123 PCFATDIRENTRYUNION paEntries = NULL;
3124 int rc = rtFsFatDirShrd_GetEntriesAt(pThis, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
3125 if (RT_FAILURE(rc))
3126 return rc;
3127
3128 /*
3129 * Now work thru each of the entries.
3130 */
3131 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
3132 {
3133 switch ((uint8_t)paEntries[iEntry].Entry.achName[0])
3134 {
3135 default:
3136 break;
3137 case FATDIRENTRY_CH0_DELETED:
3138 cwcName = 0;
3139 continue;
3140 case FATDIRENTRY_CH0_END_OF_DIR:
3141 if (pThis->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
3142 {
3143 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3144 return VERR_FILE_NOT_FOUND;
3145 }
3146 cwcName = 0;
3147 break; /* Technically a valid entry before DOS 2.0, or so some claim. */
3148 }
3149
3150 /*
3151 * Check for long filename slot.
3152 */
3153 if ( paEntries[iEntry].Slot.fAttrib == FAT_ATTR_NAME_SLOT
3154 && paEntries[iEntry].Slot.idxZero == 0
3155 && paEntries[iEntry].Slot.fZero == 0
3156 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) <= FATDIRNAMESLOT_HIGHEST_SLOT_ID
3157 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) != 0)
3158 {
3159 /* New slot? */
3160 if (paEntries[iEntry].Slot.idSlot & FATDIRNAMESLOT_FIRST_SLOT_FLAG)
3161 {
3162 idNextSlot = paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG;
3163 bChecksum = paEntries[iEntry].Slot.bChecksum;
3164 cwcName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
3165 wszName[cwcName] = '\0';
3166 }
3167 /* Is valid next entry? */
3168 else if ( paEntries[iEntry].Slot.idSlot == idNextSlot
3169 && paEntries[iEntry].Slot.bChecksum == bChecksum)
3170 { /* likely */ }
3171 else
3172 cwcName = 0;
3173 if (cwcName)
3174 {
3175 idNextSlot--;
3176 size_t offName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
3177 memcpy(&wszName[offName], paEntries[iEntry].Slot.awcName0, sizeof(paEntries[iEntry].Slot.awcName0));
3178 memcpy(&wszName[offName + 5], paEntries[iEntry].Slot.awcName1, sizeof(paEntries[iEntry].Slot.awcName1));
3179 memcpy(&wszName[offName + 5 + 6], paEntries[iEntry].Slot.awcName2, sizeof(paEntries[iEntry].Slot.awcName2));
3180 }
3181 }
3182 /*
3183 * Regular directory entry. Do the matching, first 8.3 then long name.
3184 */
3185 else if ( fIs8Dot3Name
3186 && !(paEntries[iEntry].Entry.fAttrib & FAT_ATTR_VOLUME)
3187 && memcmp(paEntries[iEntry].Entry.achName, szName8Dot3, sizeof(paEntries[iEntry].Entry.achName)) == 0)
3188 {
3189 *poffEntryInDir = offEntryInDir;
3190 *pDirEntry = paEntries[iEntry].Entry;
3191 *pfLong = false;
3192 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3193 return VINF_SUCCESS;
3194 }
3195 else if ( cwcName != 0
3196 && idNextSlot == 0
3197 && !(paEntries[iEntry].Entry.fAttrib & FAT_ATTR_VOLUME)
3198 && rtFsFatDir_CalcChecksum(&paEntries[iEntry].Entry) == bChecksum
3199 && RTUtf16ICmpUtf8(wszName, pszEntry) == 0)
3200 {
3201 *poffEntryInDir = offEntryInDir;
3202 *pDirEntry = paEntries[iEntry].Entry;
3203 *pfLong = true;
3204 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3205 return VINF_SUCCESS;
3206 }
3207 else
3208 cwcName = 0;
3209 }
3210
3211 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3212 }
3213
3214 return VERR_FILE_NOT_FOUND;
3215}
3216
3217
3218/**
3219 * Watered down version of rtFsFatDirShrd_FindEntry that is used by the short name
3220 * generator to check for duplicates.
3221 *
3222 * @returns IPRT status code.
3223 * @retval VERR_FILE_NOT_FOUND if not found.
3224 * @retval VINF_SUCCESS if found.
3225 * @param pThis The directory to search.
3226 * @param pszEntry The entry to look for.
3227 */
3228static int rtFsFatDirShrd_FindEntryShort(PRTFSFATDIRSHRD pThis, const char *pszName8Dot3)
3229{
3230 Assert(strlen(pszName8Dot3) == 8+3);
3231
3232 /*
3233 * Scan the directory buffer by buffer.
3234 */
3235 uint32_t offEntryInDir = 0;
3236 uint32_t const cbDir = pThis->Core.cbObject;
3237 Assert(RT_ALIGN_32(cbDir, sizeof(FATDIRENTRY)) == cbDir);
3238
3239 while (offEntryInDir < cbDir)
3240 {
3241 /* Get chunk of entries starting at offEntryInDir. */
3242 uint32_t uBufferLock = UINT32_MAX;
3243 uint32_t cEntries = 0;
3244 PCFATDIRENTRYUNION paEntries = NULL;
3245 int rc = rtFsFatDirShrd_GetEntriesAt(pThis, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
3246 if (RT_FAILURE(rc))
3247 return rc;
3248
3249 /*
3250 * Now work thru each of the entries.
3251 */
3252 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
3253 {
3254 switch ((uint8_t)paEntries[iEntry].Entry.achName[0])
3255 {
3256 default:
3257 break;
3258 case FATDIRENTRY_CH0_DELETED:
3259 continue;
3260 case FATDIRENTRY_CH0_END_OF_DIR:
3261 if (pThis->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
3262 {
3263 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3264 return VERR_FILE_NOT_FOUND;
3265 }
3266 break; /* Technically a valid entry before DOS 2.0, or so some claim. */
3267 }
3268
3269 /*
3270 * Skip long filename slots.
3271 */
3272 if ( paEntries[iEntry].Slot.fAttrib == FAT_ATTR_NAME_SLOT
3273 && paEntries[iEntry].Slot.idxZero == 0
3274 && paEntries[iEntry].Slot.fZero == 0
3275 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) <= FATDIRNAMESLOT_HIGHEST_SLOT_ID
3276 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) != 0)
3277 { /* skipped */ }
3278 /*
3279 * Regular directory entry. Do the matching, first 8.3 then long name.
3280 */
3281 else if (memcmp(paEntries[iEntry].Entry.achName, pszName8Dot3, sizeof(paEntries[iEntry].Entry.achName)) == 0)
3282 {
3283 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3284 return VINF_SUCCESS;
3285 }
3286 }
3287
3288 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3289 }
3290
3291 return VERR_FILE_NOT_FOUND;
3292}
3293
3294
3295/**
3296 * Calculates the FATDIRENTRY::fCase flags for the given name.
3297 *
3298 * ASSUMES that the name is a 8.3 name.
3299 *
3300 * @returns Case flag mask.
3301 * @param pszName The name.
3302 */
3303static uint8_t rtFsFatDir_CalcCaseFlags(const char *pszName)
3304{
3305 uint8_t bRet = FATDIRENTRY_CASE_F_LOWER_BASE | FATDIRENTRY_CASE_F_LOWER_EXT;
3306 uint8_t bCurrent = FATDIRENTRY_CASE_F_LOWER_BASE;
3307 for (;;)
3308 {
3309 RTUNICP uc;
3310 int rc = RTStrGetCpEx(&pszName, &uc);
3311 if (RT_SUCCESS(rc))
3312 {
3313 if (uc != 0)
3314 {
3315 if (uc != '.')
3316 {
3317 if (RTUniCpIsUpper(uc))
3318 {
3319 bRet &= ~bCurrent;
3320 if (!bRet)
3321 return 0;
3322 }
3323 }
3324 else
3325 bCurrent = FATDIRENTRY_CASE_F_LOWER_EXT;
3326 }
3327 else if (bCurrent == FATDIRENTRY_CASE_F_LOWER_BASE)
3328 return bRet & ~FATDIRENTRY_CASE_F_LOWER_EXT;
3329 else
3330 return bRet;
3331 }
3332 else
3333 return 0;
3334 }
3335}
3336
3337
3338/**
3339 * Checks if we need to generate a long name for @a pszEntry.
3340 *
3341 * @returns true if we need to, false if we don't.
3342 * @param pszEntry The UTF-8 directory entry entry name.
3343 * @param fIs8Dot3Name Whether we've managed to create a 8-dot-3 name.
3344 * @param pDirEntry The directory entry with the 8-dot-3 name when
3345 * fIs8Dot3Name is set.
3346 */
3347static bool rtFsFatDir_NeedLongName(const char *pszEntry, bool fIs8Dot3Name, PCFATDIRENTRY pDirEntry)
3348{
3349 /*
3350 * Check the easy ways out first.
3351 */
3352
3353 /* If we couldn't make a straight 8-dot-3 name out of it, the we
3354 must do the long name thing. No question. */
3355 if (!fIs8Dot3Name)
3356 return true;
3357
3358 /* If both lower case flags are set, then the whole name must be
3359 lowercased, so we won't need a long entry. */
3360 if (pDirEntry->fCase == (FATDIRENTRY_CASE_F_LOWER_BASE | FATDIRENTRY_CASE_F_LOWER_EXT))
3361 return false;
3362
3363 /*
3364 * Okay, check out the whole string then, part by part. (This is code
3365 * similar to rtFsFatDir_CalcCaseFlags.)
3366 */
3367 uint8_t fCurrent = pDirEntry->fCase & FATDIRENTRY_CASE_F_LOWER_BASE;
3368 for (;;)
3369 {
3370 RTUNICP uc;
3371 int rc = RTStrGetCpEx(&pszEntry, &uc);
3372 if (RT_SUCCESS(rc))
3373 {
3374 if (uc != 0)
3375 {
3376 if (uc != '.')
3377 {
3378 if ( fCurrent
3379 || !RTUniCpIsLower(uc))
3380 { /* okay */ }
3381 else
3382 return true;
3383 }
3384 else
3385 fCurrent = pDirEntry->fCase & FATDIRENTRY_CASE_F_LOWER_EXT;
3386 }
3387 /* It checked out to the end, so we don't need a long name. */
3388 else
3389 return false;
3390 }
3391 else
3392 return true;
3393 }
3394}
3395
3396
3397/**
3398 * Checks if the given long name is valid for a long file name or not.
3399 *
3400 * Encoding, length and character set limitations are checked.
3401 *
3402 * @returns IRPT status code.
3403 * @param pwszEntry The long filename.
3404 * @param cwc The length of the filename in UTF-16 chars.
3405 */
3406static int rtFsFatDir_ValidateLongName(PCRTUTF16 pwszEntry, size_t cwc)
3407{
3408 /* Length limitation. */
3409 if (cwc <= RTFSFAT_MAX_LFN_CHARS)
3410 {
3411 /* Character set limitations. */
3412 for (size_t off = 0; off < cwc; off++)
3413 {
3414 RTUTF16 wc = pwszEntry[off];
3415 if (wc < 128)
3416 {
3417 if (g_awchFatCp437ValidChars[wc] <= UINT16_C(0xfffe))
3418 { /* likely */ }
3419 else
3420 return VERR_INVALID_NAME;
3421 }
3422 }
3423
3424 /* Name limitations. */
3425 if ( cwc == 1
3426 && pwszEntry[0] == '.')
3427 return VERR_INVALID_NAME;
3428 if ( cwc == 2
3429 && pwszEntry[0] == '.'
3430 && pwszEntry[1] == '.')
3431 return VERR_INVALID_NAME;
3432
3433 /** @todo Check for more invalid names, also in the 8.3 case! */
3434 return VINF_SUCCESS;
3435 }
3436 return VERR_FILENAME_TOO_LONG;
3437}
3438
3439
3440/**
3441 * Worker for rtFsFatDirShrd_GenerateShortName.
3442 */
3443static void rtFsFatDir_CopyShortName(char *pszDst, uint32_t cchDst, const char *pszSrc, size_t cchSrc, char chPad)
3444{
3445 /* Copy from source. */
3446 if (cchSrc > 0)
3447 {
3448 const char *pszSrcEnd = &pszSrc[cchSrc];
3449 while (cchDst > 0 && pszSrc != pszSrcEnd)
3450 {
3451 RTUNICP uc;
3452 int rc = RTStrGetCpEx(&pszSrc, &uc);
3453 if (RT_SUCCESS(rc))
3454 {
3455 if (uc < 128)
3456 {
3457 if (g_awchFatCp437ValidChars[uc] != uc)
3458 {
3459 if (uc)
3460 {
3461 uc = RTUniCpToUpper(uc);
3462 if (g_awchFatCp437ValidChars[uc] != uc)
3463 uc = '_';
3464 }
3465 else
3466 break;
3467 }
3468 }
3469 else
3470 uc = '_';
3471 }
3472 else
3473 uc = '_';
3474
3475 *pszDst++ = (char)uc;
3476 cchDst--;
3477 }
3478 }
3479
3480 /* Pad the remaining space. */
3481 while (cchDst-- > 0)
3482 *pszDst++ = chPad;
3483}
3484
3485
3486/**
3487 * Generates a short filename.
3488 *
3489 * @returns IPRT status code.
3490 * @param pThis The directory.
3491 * @param pszEntry The long name (UTF-8).
3492 * @param pDirEntry Where to put the short name.
3493 */
3494static int rtFsFatDirShrd_GenerateShortName(PRTFSFATDIRSHRD pThis, const char *pszEntry, PFATDIRENTRY pDirEntry)
3495{
3496 /* Do some input parsing. */
3497 const char *pszExt = RTPathSuffix(pszEntry);
3498 size_t const cchBasename = pszExt ? pszExt - pszEntry : strlen(pszEntry);
3499 size_t const cchExt = pszExt ? strlen(++pszExt) : 0;
3500
3501 /* Fill in the extension first. It stays the same. */
3502 char szShortName[8+3+1];
3503 rtFsFatDir_CopyShortName(&szShortName[8], 3, pszExt, cchExt, ' ');
3504 szShortName[8+3] = '\0';
3505
3506 /*
3507 * First try single digit 1..9.
3508 */
3509 rtFsFatDir_CopyShortName(szShortName, 6, pszEntry, cchBasename, '_');
3510 szShortName[6] = '~';
3511 for (uint32_t iLastDigit = 1; iLastDigit < 10; iLastDigit++)
3512 {
3513 szShortName[7] = iLastDigit + '0';
3514 int rc = rtFsFatDirShrd_FindEntryShort(pThis, szShortName);
3515 if (rc == VERR_FILE_NOT_FOUND)
3516 {
3517 memcpy(pDirEntry->achName, szShortName, sizeof(pDirEntry->achName));
3518 return VINF_SUCCESS;
3519 }
3520 if (RT_FAILURE(rc))
3521 return rc;
3522 }
3523
3524 /*
3525 * First try two digits 10..99.
3526 */
3527 szShortName[5] = '~';
3528 for (uint32_t iFirstDigit = 1; iFirstDigit < 10; iFirstDigit++)
3529 for (uint32_t iLastDigit = 0; iLastDigit < 10; iLastDigit++)
3530 {
3531 szShortName[6] = iFirstDigit + '0';
3532 szShortName[7] = iLastDigit + '0';
3533 int rc = rtFsFatDirShrd_FindEntryShort(pThis, szShortName);
3534 if (rc == VERR_FILE_NOT_FOUND)
3535 {
3536 memcpy(pDirEntry->achName, szShortName, sizeof(pDirEntry->achName));
3537 return VINF_SUCCESS;
3538 }
3539 if (RT_FAILURE(rc))
3540 return rc;
3541 }
3542
3543 /*
3544 * Okay, do random numbers then.
3545 */
3546 szShortName[2] = '~';
3547 for (uint32_t i = 0; i < 8192; i++)
3548 {
3549 char szHex[68];
3550 ssize_t cchHex = RTStrFormatU32(szHex, sizeof(szHex), RTRandU32(), 16, 5, 0, RTSTR_F_CAPITAL | RTSTR_F_WIDTH | RTSTR_F_ZEROPAD);
3551 AssertReturn(cchHex >= 5, VERR_NET_NOT_UNIQUE_NAME);
3552 szShortName[7] = szHex[cchHex - 1];
3553 szShortName[6] = szHex[cchHex - 2];
3554 szShortName[5] = szHex[cchHex - 3];
3555 szShortName[4] = szHex[cchHex - 4];
3556 szShortName[3] = szHex[cchHex - 5];
3557 int rc = rtFsFatDirShrd_FindEntryShort(pThis, szShortName);
3558 if (rc == VERR_FILE_NOT_FOUND)
3559 {
3560 memcpy(pDirEntry->achName, szShortName, sizeof(pDirEntry->achName));
3561 return VINF_SUCCESS;
3562 }
3563 if (RT_FAILURE(rc))
3564 return rc;
3565 }
3566
3567 return VERR_NET_NOT_UNIQUE_NAME;
3568}
3569
3570
3571/**
3572 * Considers whether we need to create a long name or not.
3573 *
3574 * If a long name is needed and the name wasn't 8-dot-3 compatible, a 8-dot-3
3575 * name will be generated and stored in *pDirEntry.
3576 *
3577 * @returns IPRT status code
3578 * @param pThis The directory.
3579 * @param pszEntry The name.
3580 * @param fIs8Dot3Name Whether we have a 8-dot-3 name already.
3581 * @param pDirEntry Where to return the generated 8-dot-3 name.
3582 * @param paSlots Where to return the long name entries. The array
3583 * can hold at least FATDIRNAMESLOT_MAX_SLOTS entries.
3584 * @param pcSlots Where to return the actual number of slots used.
3585 */
3586static int rtFsFatDirShrd_MaybeCreateLongNameAndShortAlias(PRTFSFATDIRSHRD pThis, const char *pszEntry, bool fIs8Dot3Name,
3587 PFATDIRENTRY pDirEntry, PFATDIRNAMESLOT paSlots, uint32_t *pcSlots)
3588{
3589 RT_NOREF(pThis, pDirEntry, paSlots, pszEntry);
3590
3591 /*
3592 * If we don't need to create a long name, return immediately.
3593 */
3594 if (!rtFsFatDir_NeedLongName(pszEntry, fIs8Dot3Name, pDirEntry))
3595 {
3596 *pcSlots = 0;
3597 return VINF_SUCCESS;
3598 }
3599
3600 /*
3601 * Convert the name to UTF-16 and figure it's length (this validates the
3602 * input encoding). Then do long name validation (length, charset limitation).
3603 */
3604 RTUTF16 wszEntry[FATDIRNAMESLOT_MAX_SLOTS * FATDIRNAMESLOT_CHARS_PER_SLOT + 4];
3605 PRTUTF16 pwszEntry = wszEntry;
3606 size_t cwcEntry;
3607 int rc = RTStrToUtf16Ex(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(wszEntry), &cwcEntry);
3608 if (RT_SUCCESS(rc))
3609 rc = rtFsFatDir_ValidateLongName(pwszEntry, cwcEntry);
3610 if (RT_SUCCESS(rc))
3611 {
3612 /*
3613 * Generate a short name if we need to.
3614 */
3615 if (!fIs8Dot3Name)
3616 rc = rtFsFatDirShrd_GenerateShortName(pThis, pszEntry, pDirEntry);
3617 if (RT_SUCCESS(rc))
3618 {
3619 /*
3620 * Fill in the long name slots. First we pad the wszEntry with 0xffff
3621 * until it is a multiple of of the slot count. That way we can copy
3622 * the name straight into the entry without constaints.
3623 */
3624 memset(&wszEntry[cwcEntry + 1], 0xff,
3625 RT_MIN(sizeof(wszEntry) - (cwcEntry + 1) * sizeof(RTUTF16),
3626 FATDIRNAMESLOT_CHARS_PER_SLOT * sizeof(RTUTF16)));
3627
3628 uint8_t const bChecksum = rtFsFatDir_CalcChecksum(pDirEntry);
3629 size_t const cSlots = (cwcEntry + FATDIRNAMESLOT_CHARS_PER_SLOT - 1) / FATDIRNAMESLOT_CHARS_PER_SLOT;
3630 size_t iSlot = cSlots;
3631 PCRTUTF16 pwszSrc = wszEntry;
3632 while (iSlot-- > 0)
3633 {
3634 memcpy(paSlots[iSlot].awcName0, pwszSrc, sizeof(paSlots[iSlot].awcName0));
3635 pwszSrc += RT_ELEMENTS(paSlots[iSlot].awcName0);
3636 memcpy(paSlots[iSlot].awcName1, pwszSrc, sizeof(paSlots[iSlot].awcName1));
3637 pwszSrc += RT_ELEMENTS(paSlots[iSlot].awcName1);
3638 memcpy(paSlots[iSlot].awcName2, pwszSrc, sizeof(paSlots[iSlot].awcName2));
3639 pwszSrc += RT_ELEMENTS(paSlots[iSlot].awcName2);
3640
3641 paSlots[iSlot].idSlot = (uint8_t)(cSlots - iSlot);
3642 paSlots[iSlot].fAttrib = FAT_ATTR_NAME_SLOT;
3643 paSlots[iSlot].fZero = 0;
3644 paSlots[iSlot].idxZero = 0;
3645 paSlots[iSlot].bChecksum = bChecksum;
3646 }
3647 paSlots[0].idSlot |= FATDIRNAMESLOT_FIRST_SLOT_FLAG;
3648 *pcSlots = (uint32_t)cSlots;
3649 return VINF_SUCCESS;
3650 }
3651 }
3652 *pcSlots = UINT32_MAX;
3653 return rc;
3654}
3655
3656
3657/**
3658 * Searches the directory for a given number of free directory entries.
3659 *
3660 * The free entries must be consecutive of course.
3661 *
3662 * @returns IPRT status code.
3663 * @retval VERR_DISK_FULL if no space was found, *pcFreeTail set.
3664 * @param pThis The directory to search.
3665 * @param cEntriesNeeded How many entries we need.
3666 * @param poffEntryInDir Where to return the offset of the first entry we
3667 * found.
3668 * @param pcFreeTail Where to return the number of free entries at the
3669 * end of the directory when VERR_DISK_FULL is
3670 * returned.
3671 */
3672static int rtFsFatChain_FindFreeEntries(PRTFSFATDIRSHRD pThis, uint32_t cEntriesNeeded,
3673 uint32_t *poffEntryInDir, uint32_t *pcFreeTail)
3674{
3675 /* First try make gcc happy. */
3676 *pcFreeTail = 0;
3677 *poffEntryInDir = UINT32_MAX;
3678
3679 /*
3680 * Scan the whole directory, buffer by buffer.
3681 */
3682 uint32_t offStartFreeEntries = UINT32_MAX;
3683 uint32_t cFreeEntries = 0;
3684 uint32_t offEntryInDir = 0;
3685 uint32_t const cbDir = pThis->Core.cbObject;
3686 Assert(RT_ALIGN_32(cbDir, sizeof(FATDIRENTRY)) == cbDir);
3687 while (offEntryInDir < cbDir)
3688 {
3689 /* Get chunk of entries starting at offEntryInDir. */
3690 uint32_t uBufferLock = UINT32_MAX;
3691 uint32_t cEntries = 0;
3692 PCFATDIRENTRYUNION paEntries = NULL;
3693 int rc = rtFsFatDirShrd_GetEntriesAt(pThis, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
3694 if (RT_FAILURE(rc))
3695 return rc;
3696
3697 /*
3698 * Now work thru each of the entries.
3699 */
3700 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
3701 {
3702 uint8_t const bFirst = paEntries[iEntry].Entry.achName[0];
3703 if ( bFirst == FATDIRENTRY_CH0_DELETED
3704 || bFirst == FATDIRENTRY_CH0_END_OF_DIR)
3705 {
3706 if (offStartFreeEntries != UINT32_MAX)
3707 cFreeEntries++;
3708 else
3709 {
3710 offStartFreeEntries = offEntryInDir;
3711 cFreeEntries = 1;
3712 }
3713 if (cFreeEntries >= cEntriesNeeded)
3714 {
3715 *pcFreeTail = cEntriesNeeded;
3716 *poffEntryInDir = offStartFreeEntries;
3717 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3718 return VINF_SUCCESS;
3719 }
3720
3721 if (bFirst == FATDIRENTRY_CH0_END_OF_DIR)
3722 {
3723 if (pThis->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
3724 {
3725 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3726 *pcFreeTail = cFreeEntries = (cbDir - offStartFreeEntries) / sizeof(FATDIRENTRY);
3727 if (cFreeEntries >= cEntriesNeeded)
3728 {
3729 *poffEntryInDir = offStartFreeEntries;
3730 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3731 return VINF_SUCCESS;
3732 }
3733 return VERR_DISK_FULL;
3734 }
3735 }
3736 }
3737 else if (offStartFreeEntries != UINT32_MAX)
3738 {
3739 offStartFreeEntries = UINT32_MAX;
3740 cFreeEntries = 0;
3741 }
3742 }
3743 rtFsFatDirShrd_ReleaseBufferAfterReading(pThis, uBufferLock);
3744 }
3745 *pcFreeTail = cFreeEntries;
3746 return VERR_DISK_FULL;
3747}
3748
3749
3750/**
3751 * Try grow the directory.
3752 *
3753 * This is not called on the root directory.
3754 *
3755 * @returns IPRT status code.
3756 * @retval VERR_DISK_FULL if we failed to allocated new space.
3757 * @param pThis The directory to grow.
3758 * @param cMinNewEntries The minimum number of new entries to allocated.
3759 */
3760static int rtFsFatChain_GrowDirectory(PRTFSFATDIRSHRD pThis, uint32_t cMinNewEntries)
3761{
3762 RT_NOREF(pThis, cMinNewEntries);
3763 return VERR_DISK_FULL;
3764}
3765
3766
3767/**
3768 * Inserts a directory with zero of more long name slots preceeding it.
3769 *
3770 * @returns IPRT status code.
3771 * @param pThis The directory.
3772 * @param pDirEntry The directory entry.
3773 * @param paSlots The long name slots.
3774 * @param cSlots The number of long name slots.
3775 * @param poffEntryInDir Where to return the directory offset.
3776 */
3777static int rtFsFatChain_InsertEntries(PRTFSFATDIRSHRD pThis, PCFATDIRENTRY pDirEntry, PFATDIRNAMESLOT paSlots, uint32_t cSlots,
3778 uint32_t *poffEntryInDir)
3779{
3780 uint32_t const cTotalEntries = cSlots + 1;
3781
3782 /*
3783 * Find somewhere to put the entries. Try extend the directory if we're
3784 * not successful at first.
3785 */
3786 uint32_t cFreeTailEntries;
3787 uint32_t offFirstInDir;
3788 int rc = rtFsFatChain_FindFreeEntries(pThis, cTotalEntries, &offFirstInDir, &cFreeTailEntries);
3789 if (rc == VERR_DISK_FULL)
3790 {
3791 Assert(cFreeTailEntries < cTotalEntries);
3792
3793 /* Try grow it and use the newly allocated space. */
3794 if ( pThis->Core.pParentDir
3795 && pThis->cEntries < _64K /* Don't grow beyond 64K entries */)
3796 {
3797 offFirstInDir = pThis->Core.cbObject - cFreeTailEntries * sizeof(FATDIRENTRY);
3798 rc = rtFsFatChain_GrowDirectory(pThis, cTotalEntries - cFreeTailEntries);
3799 }
3800
3801 if (rc == VERR_DISK_FULL)
3802 {
3803 /** @todo Try compact the directory if we couldn't grow it. */
3804 }
3805 }
3806 if (RT_SUCCESS(rc))
3807 {
3808 /*
3809 * Update the directory.
3810 */
3811 uint32_t offCurrent = offFirstInDir;
3812 for (uint32_t iSrcSlot = 0; iSrcSlot < cTotalEntries; iSrcSlot++, offCurrent += sizeof(FATDIRENTRY))
3813 {
3814 uint32_t uBufferLock;
3815 PFATDIRENTRY pDstEntry;
3816 rc = rtFsFatDirShrd_GetEntryForUpdate(pThis, offCurrent, &pDstEntry, &uBufferLock);
3817 if (RT_SUCCESS(rc))
3818 {
3819 if (iSrcSlot < cSlots)
3820 memcpy(pDstEntry, &paSlots[iSrcSlot], sizeof(*pDstEntry));
3821 else
3822 memcpy(pDstEntry, pDirEntry, sizeof(*pDstEntry));
3823 rc = rtFsFatDirShrd_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
3824 if (RT_SUCCESS(rc))
3825 continue;
3826
3827 /*
3828 * Bail out: Try mark any edited entries as deleted.
3829 */
3830 iSrcSlot++;
3831 }
3832 while (iSrcSlot-- > 0)
3833 {
3834 int rc2 = rtFsFatDirShrd_GetEntryForUpdate(pThis, offFirstInDir + iSrcSlot * sizeof(FATDIRENTRY),
3835 &pDstEntry, &uBufferLock);
3836 if (RT_SUCCESS(rc2))
3837 {
3838 pDstEntry->achName[0] = FATDIRENTRY_CH0_DELETED;
3839 rtFsFatDirShrd_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
3840 }
3841 }
3842 *poffEntryInDir = UINT32_MAX;
3843 return rc;
3844 }
3845 AssertRC(rc);
3846
3847 /*
3848 * Successfully inserted all.
3849 */
3850 *poffEntryInDir = offFirstInDir + cSlots * sizeof(FATDIRENTRY);
3851 return VINF_SUCCESS;
3852 }
3853
3854 *poffEntryInDir = UINT32_MAX;
3855 return rc;
3856}
3857
3858
3859
3860/**
3861 * Creates a new directory entry.
3862 *
3863 * @returns IPRT status code
3864 * @param pThis The directory.
3865 * @param pszEntry The name of the new entry.
3866 * @param fAttrib The attributes.
3867 * @param cbInitial The initialize size.
3868 * @param poffEntryInDir Where to return the offset of the directory entry.
3869 * @param pDirEntry Where to return a copy of the directory entry.
3870 *
3871 * @remarks ASSUMES caller has already called rtFsFatDirShrd_FindEntry to make sure
3872 * the entry doesn't exist.
3873 */
3874static int rtFsFatDirShrd_CreateEntry(PRTFSFATDIRSHRD pThis, const char *pszEntry, uint8_t fAttrib, uint32_t cbInitial,
3875 uint32_t *poffEntryInDir, PFATDIRENTRY pDirEntry)
3876{
3877 PRTFSFATVOL pVol = pThis->Core.pVol;
3878 *poffEntryInDir = UINT32_MAX;
3879 if (pVol->fReadOnly)
3880 return VERR_WRITE_PROTECT;
3881
3882 /*
3883 * Create the directory entries on the stack.
3884 */
3885 bool fIs8Dot3Name = rtFsFatDir_StringTo8Dot3((char *)pDirEntry->achName, pszEntry);
3886 pDirEntry->fAttrib = fAttrib;
3887 pDirEntry->fCase = fIs8Dot3Name ? rtFsFatDir_CalcCaseFlags(pszEntry) : 0;
3888 pDirEntry->uBirthCentiseconds = rtFsFatCurrentFatDateTime(pVol, &pDirEntry->uBirthDate, &pDirEntry->uBirthTime);
3889 pDirEntry->uAccessDate = pDirEntry->uBirthDate;
3890 pDirEntry->uModifyDate = pDirEntry->uBirthDate;
3891 pDirEntry->uModifyTime = pDirEntry->uBirthTime;
3892 pDirEntry->idxCluster = 0; /* Will fill this in later if cbInitial is non-zero. */
3893 pDirEntry->u.idxClusterHigh = 0;
3894 pDirEntry->cbFile = cbInitial;
3895
3896 /*
3897 * Create long filename slots if necessary.
3898 */
3899 uint32_t cSlots = UINT32_MAX;
3900 FATDIRNAMESLOT aSlots[FATDIRNAMESLOT_MAX_SLOTS];
3901 AssertCompile(RTFSFAT_MAX_LFN_CHARS < RT_ELEMENTS(aSlots) * FATDIRNAMESLOT_CHARS_PER_SLOT);
3902 int rc = rtFsFatDirShrd_MaybeCreateLongNameAndShortAlias(pThis, pszEntry, fIs8Dot3Name, pDirEntry, aSlots, &cSlots);
3903 if (RT_SUCCESS(rc))
3904 {
3905 Assert(cSlots <= FATDIRNAMESLOT_MAX_SLOTS);
3906
3907 /*
3908 * Allocate initial clusters if requested.
3909 */
3910 RTFSFATCHAIN Clusters;
3911 rtFsFatChain_InitEmpty(&Clusters, pVol);
3912 if (cbInitial > 0)
3913 {
3914 rc = rtFsFatClusterMap_AllocateMoreClusters(pVol, &Clusters,
3915 (cbInitial + Clusters.cbCluster - 1) >> Clusters.cClusterByteShift);
3916 if (RT_SUCCESS(rc))
3917 {
3918 uint32_t idxFirstCluster = rtFsFatChain_GetFirstCluster(&Clusters);
3919 pDirEntry->idxCluster = (uint16_t)idxFirstCluster;
3920 if (pVol->enmFatType >= RTFSFATTYPE_FAT32)
3921 pDirEntry->u.idxClusterHigh = (uint16_t)(idxFirstCluster >> 16);
3922 }
3923 }
3924 if (RT_SUCCESS(rc))
3925 {
3926 /*
3927 * Insert the directory entry and name slots.
3928 */
3929 rc = rtFsFatChain_InsertEntries(pThis, pDirEntry, aSlots, cSlots, poffEntryInDir);
3930 if (RT_SUCCESS(rc))
3931 {
3932 rtFsFatChain_Delete(&Clusters);
3933 return VINF_SUCCESS;
3934 }
3935
3936 for (uint32_t iClusterToFree = 0; iClusterToFree < Clusters.cClusters; iClusterToFree++)
3937 rtFsFatClusterMap_FreeCluster(pVol, rtFsFatChain_GetClusterByIndex(&Clusters, iClusterToFree));
3938 rtFsFatChain_Delete(&Clusters);
3939 }
3940 }
3941 return rc;
3942}
3943
3944
3945/**
3946 * Releases a reference to a shared directory structure.
3947 *
3948 * @param pShared The shared directory structure.
3949 */
3950static int rtFsFatDirShrd_Release(PRTFSFATDIRSHRD pShared)
3951{
3952 uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
3953 Assert(cRefs < UINT32_MAX / 2);
3954 if (cRefs == 0)
3955 {
3956 LogFlow(("rtFsFatDirShrd_Release: Destroying shared structure %p\n", pShared));
3957 Assert(pShared->Core.cRefs == 0);
3958
3959 int rc;
3960 if (pShared->paEntries)
3961 {
3962 rc = rtFsFatDirShrd_Flush(pShared);
3963 RTMemFree(pShared->paEntries);
3964 pShared->paEntries = NULL;
3965 }
3966 else
3967 rc = VINF_SUCCESS;
3968
3969 if ( pShared->fFullyBuffered
3970 && pShared->u.Full.pbDirtySectors)
3971 {
3972 RTMemFree(pShared->u.Full.pbDirtySectors);
3973 pShared->u.Full.pbDirtySectors = NULL;
3974 }
3975
3976 int rc2 = rtFsFatObj_Close(&pShared->Core);
3977 if (RT_SUCCESS(rc))
3978 rc = rc2;
3979
3980 RTMemFree(pShared);
3981 return rc;
3982 }
3983 return VINF_SUCCESS;
3984}
3985
3986
3987/**
3988 * Retains a reference to a shared directory structure.
3989 *
3990 * @param pShared The shared directory structure.
3991 */
3992static void rtFsFatDirShrd_Retain(PRTFSFATDIRSHRD pShared)
3993{
3994 uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
3995 Assert(cRefs > 1); NOREF(cRefs);
3996}
3997
3998
3999/**
4000 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
4001 */
4002static DECLCALLBACK(int) rtFsFatDir_Close(void *pvThis)
4003{
4004 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
4005 PRTFSFATDIRSHRD pShared = pThis->pShared;
4006 pThis->pShared = NULL;
4007 if (pShared)
4008 return rtFsFatDirShrd_Release(pShared);
4009 return VINF_SUCCESS;
4010}
4011
4012
4013/**
4014 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
4015 */
4016static DECLCALLBACK(int) rtFsFatDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4017{
4018 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
4019 return rtFsFatObj_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
4020}
4021
4022
4023/**
4024 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
4025 */
4026static DECLCALLBACK(int) rtFsFatDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
4027{
4028 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
4029 return rtFsFatObj_SetMode(&pThis->pShared->Core, fMode, fMask);
4030}
4031
4032
4033/**
4034 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
4035 */
4036static DECLCALLBACK(int) rtFsFatDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
4037 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
4038{
4039 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
4040 return rtFsFatObj_SetTimes(&pThis->pShared->Core, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
4041}
4042
4043
4044/**
4045 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
4046 */
4047static DECLCALLBACK(int) rtFsFatDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
4048{
4049 RT_NOREF(pvThis, uid, gid);
4050 return VERR_NOT_SUPPORTED;
4051}
4052
4053
4054/**
4055 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
4056 */
4057static DECLCALLBACK(int) rtFsFatDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
4058 uint32_t fFlags, PRTVFSOBJ phVfsObj)
4059{
4060 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
4061 PRTFSFATDIRSHRD pShared = pThis->pShared;
4062 int rc;
4063
4064 /*
4065 * Special cases '.' and '.'
4066 */
4067 if (pszEntry[0] == '.')
4068 {
4069 PRTFSFATDIRSHRD pSharedToOpen;
4070 if (pszEntry[1] == '\0')
4071 pSharedToOpen = pShared;
4072 else if (pszEntry[1] == '.' && pszEntry[2] == '\0')
4073 {
4074 pSharedToOpen = pShared->Core.pParentDir;
4075 if (!pSharedToOpen)
4076 pSharedToOpen = pShared;
4077 }
4078 else
4079 pSharedToOpen = NULL;
4080 if (pSharedToOpen)
4081 {
4082 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
4083 {
4084 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
4085 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
4086 {
4087 rtFsFatDirShrd_Retain(pSharedToOpen);
4088 RTVFSDIR hVfsDir;
4089 rc = rtFsFatDir_NewWithShared(pShared->Core.pVol, pSharedToOpen, &hVfsDir);
4090 if (RT_SUCCESS(rc))
4091 {
4092 *phVfsObj = RTVfsObjFromDir(hVfsDir);
4093 RTVfsDirRelease(hVfsDir);
4094 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
4095 }
4096 }
4097 else
4098 rc = VERR_ACCESS_DENIED;
4099 }
4100 else
4101 rc = VERR_IS_A_DIRECTORY;
4102 return rc;
4103 }
4104 }
4105
4106 /*
4107 * Try open existing file.
4108 */
4109 uint32_t offEntryInDir;
4110 bool fLong;
4111 FATDIRENTRY DirEntry;
4112 rc = rtFsFatDirShrd_FindEntry(pShared, pszEntry, &offEntryInDir, &fLong, &DirEntry);
4113 if (RT_SUCCESS(rc))
4114 {
4115 switch (DirEntry.fAttrib & (FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME))
4116 {
4117 case 0:
4118 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
4119 {
4120 if ( !(DirEntry.fAttrib & FAT_ATTR_READONLY)
4121 || !(fOpen & RTFILE_O_WRITE))
4122 {
4123 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
4124 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
4125 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
4126 {
4127 RTVFSFILE hVfsFile;
4128 rc = rtFsFatFile_New(pShared->Core.pVol, pShared, &DirEntry, offEntryInDir, fOpen, &hVfsFile);
4129 if (RT_SUCCESS(rc))
4130 {
4131 *phVfsObj = RTVfsObjFromFile(hVfsFile);
4132 RTVfsFileRelease(hVfsFile);
4133 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
4134 }
4135 }
4136 else
4137 rc = VERR_ALREADY_EXISTS;
4138 }
4139 else
4140 rc = VERR_ACCESS_DENIED;
4141 }
4142 else
4143 rc = VERR_IS_A_FILE;
4144 break;
4145
4146 case FAT_ATTR_DIRECTORY:
4147 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
4148 {
4149 if ( !(DirEntry.fAttrib & FAT_ATTR_READONLY)
4150 || !(fOpen & RTFILE_O_WRITE))
4151 {
4152 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
4153 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
4154 {
4155 RTVFSDIR hVfsDir;
4156 rc = rtFsFatDir_New(pShared->Core.pVol, pShared, &DirEntry, offEntryInDir,
4157 RTFSFAT_GET_CLUSTER(&DirEntry, pShared->Core.pVol), UINT64_MAX /*offDisk*/,
4158 DirEntry.cbFile, &hVfsDir);
4159 if (RT_SUCCESS(rc))
4160 {
4161 *phVfsObj = RTVfsObjFromDir(hVfsDir);
4162 RTVfsDirRelease(hVfsDir);
4163 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
4164 }
4165 }
4166 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
4167 rc = VERR_INVALID_FUNCTION;
4168 else
4169 rc = VERR_ALREADY_EXISTS;
4170 }
4171 else
4172 rc = VERR_ACCESS_DENIED;
4173 }
4174 else
4175 rc = VERR_IS_A_DIRECTORY;
4176 break;
4177
4178 default:
4179 rc = VERR_PATH_NOT_FOUND;
4180 break;
4181 }
4182 }
4183 /*
4184 * Create a file or directory?
4185 */
4186 else if (rc == VERR_FILE_NOT_FOUND)
4187 {
4188 if ( ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
4189 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
4190 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
4191 && (fFlags & RTVFSOBJ_F_CREATE_MASK) != RTVFSOBJ_F_CREATE_NOTHING)
4192 {
4193 if ((fFlags & RTVFSOBJ_F_CREATE_MASK) == RTVFSOBJ_F_CREATE_FILE)
4194 {
4195 rc = rtFsFatDirShrd_CreateEntry(pShared, pszEntry, FAT_ATTR_ARCHIVE, 0 /*cbInitial*/, &offEntryInDir, &DirEntry);
4196 if (RT_SUCCESS(rc))
4197 {
4198 RTVFSFILE hVfsFile;
4199 rc = rtFsFatFile_New(pShared->Core.pVol, pShared, &DirEntry, offEntryInDir, fOpen, &hVfsFile);
4200 if (RT_SUCCESS(rc))
4201 {
4202 *phVfsObj = RTVfsObjFromFile(hVfsFile);
4203 RTVfsFileRelease(hVfsFile);
4204 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
4205 }
4206 }
4207 }
4208 else if ((fFlags & RTVFSOBJ_F_CREATE_MASK) == RTVFSOBJ_F_CREATE_DIRECTORY)
4209 {
4210 rc = rtFsFatDirShrd_CreateEntry(pShared, pszEntry, FAT_ATTR_ARCHIVE | FAT_ATTR_DIRECTORY,
4211 pShared->Core.pVol->cbCluster, &offEntryInDir, &DirEntry);
4212 if (RT_SUCCESS(rc))
4213 {
4214 RTVFSDIR hVfsDir;
4215 rc = rtFsFatDir_New(pShared->Core.pVol, pShared, &DirEntry, offEntryInDir,
4216 RTFSFAT_GET_CLUSTER(&DirEntry, pShared->Core.pVol), UINT64_MAX /*offDisk*/,
4217 DirEntry.cbFile, &hVfsDir);
4218 if (RT_SUCCESS(rc))
4219 {
4220 *phVfsObj = RTVfsObjFromDir(hVfsDir);
4221 RTVfsDirRelease(hVfsDir);
4222 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
4223 }
4224 }
4225 }
4226 else
4227 rc = VERR_VFS_UNSUPPORTED_CREATE_TYPE;
4228 }
4229 }
4230
4231 return rc;
4232}
4233
4234
4235/**
4236 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
4237 */
4238static DECLCALLBACK(int) rtFsFatDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
4239{
4240 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
4241 return VERR_NOT_SUPPORTED;
4242}
4243
4244
4245/**
4246 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
4247 */
4248static DECLCALLBACK(int) rtFsFatDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
4249 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
4250{
4251 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
4252 return VERR_NOT_SUPPORTED;
4253}
4254
4255
4256/**
4257 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
4258 */
4259static DECLCALLBACK(int) rtFsFatDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
4260{
4261 RT_NOREF(pvThis, pszEntry, fType);
4262 return VERR_NOT_IMPLEMENTED;
4263}
4264
4265
4266/**
4267 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
4268 */
4269static DECLCALLBACK(int) rtFsFatDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
4270{
4271 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
4272 return VERR_NOT_IMPLEMENTED;
4273}
4274
4275
4276/**
4277 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
4278 */
4279static DECLCALLBACK(int) rtFsFatDir_RewindDir(void *pvThis)
4280{
4281 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
4282 pThis->offDir = 0;
4283 return VINF_SUCCESS;
4284}
4285
4286
4287/**
4288 * Calculates the UTF-8 length of the name in the given directory entry.
4289 *
4290 * @returns The length in characters (bytes), excluding terminator.
4291 * @param pShared The shared directory structure (for codepage).
4292 * @param pEntry The directory entry.
4293 */
4294static size_t rtFsFatDir_CalcUtf8LengthForDirEntry(PRTFSFATDIRSHRD pShared, PCFATDIRENTRY pEntry)
4295{
4296 RT_NOREF(pShared);
4297 PCRTUTF16 g_pawcMap = &g_awchFatCp437Chars[0];
4298
4299 /* The base name (this won't work with DBCS, but that's not a concern at the moment). */
4300 size_t offSrc = 8;
4301 while (offSrc > 1 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[offSrc - 1]]))
4302 offSrc--;
4303
4304 size_t cchRet = 0;
4305 while (offSrc-- > 0)
4306 cchRet += RTStrCpSize(g_pawcMap[pEntry->achName[offSrc]]);
4307
4308 /* Extension. */
4309 offSrc = 11;
4310 while (offSrc > 8 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[offSrc - 1]]))
4311 offSrc--;
4312 if (offSrc > 8)
4313 {
4314 cchRet += 1; /* '.' */
4315 while (offSrc-- > 8)
4316 cchRet += RTStrCpSize(g_pawcMap[pEntry->achName[offSrc]]);
4317 }
4318
4319 return cchRet;
4320}
4321
4322
4323/**
4324 * Copies the name from the directory entry into a UTF-16 buffer.
4325 *
4326 * @returns Number of UTF-16 items written (excluding terminator).
4327 * @param pShared The shared directory structure (for codepage).
4328 * @param pEntry The directory entry.
4329 * @param pwszDst The destination buffer.
4330 * @param cwcDst The destination buffer size.
4331 */
4332static uint16_t rtFsFatDir_CopyDirEntryToUtf16(PRTFSFATDIRSHRD pShared, PCFATDIRENTRY pEntry, PRTUTF16 pwszDst, size_t cwcDst)
4333{
4334 Assert(cwcDst > 0);
4335
4336 RT_NOREF(pShared);
4337 PCRTUTF16 g_pawcMap = &g_awchFatCp437Chars[0];
4338
4339 /* The base name (this won't work with DBCS, but that's not a concern at the moment). */
4340 size_t cchSrc = 8;
4341 while (cchSrc > 1 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[cchSrc - 1]]))
4342 cchSrc--;
4343
4344 size_t offDst = 0;
4345 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
4346 {
4347 AssertReturnStmt(offDst + 1 < cwcDst, pwszDst[cwcDst - 1] = '\0', (uint16_t)cwcDst);
4348 pwszDst[offDst++] = g_pawcMap[pEntry->achName[offSrc]];
4349 }
4350
4351 /* Extension. */
4352 cchSrc = 3;
4353 while (cchSrc > 0 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[8 + cchSrc - 1]]))
4354 cchSrc--;
4355 if (cchSrc > 0)
4356 {
4357 AssertReturnStmt(offDst + 1 < cwcDst, pwszDst[cwcDst - 1] = '\0', (uint16_t)cwcDst);
4358 pwszDst[offDst++] = '.';
4359
4360 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
4361 {
4362 AssertReturnStmt(offDst + 1 < cwcDst, pwszDst[cwcDst - 1] = '\0', (uint16_t)cwcDst);
4363 pwszDst[offDst++] = g_pawcMap[pEntry->achName[8 + offSrc]];
4364 }
4365 }
4366
4367 pwszDst[offDst] = '\0';
4368 return (uint16_t)offDst;
4369}
4370
4371
4372/**
4373 * Copies the name from the directory entry into a UTF-8 buffer.
4374 *
4375 * @returns Number of UTF-16 items written (excluding terminator).
4376 * @param pShared The shared directory structure (for codepage).
4377 * @param pEntry The directory entry.
4378 * @param pszDst The destination buffer.
4379 * @param cbDst The destination buffer size.
4380 */
4381static uint16_t rtFsFatDir_CopyDirEntryToUtf8(PRTFSFATDIRSHRD pShared, PCFATDIRENTRY pEntry, char *pszDst, size_t cbDst)
4382{
4383 Assert(cbDst > 0);
4384
4385 RT_NOREF(pShared);
4386 PCRTUTF16 g_pawcMap = &g_awchFatCp437Chars[0];
4387
4388 /* The base name (this won't work with DBCS, but that's not a concern at the moment). */
4389 size_t cchSrc = 8;
4390 while (cchSrc > 1 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[cchSrc - 1]]))
4391 cchSrc--;
4392
4393 char * const pszDstEnd = pszDst + cbDst;
4394 char *pszCurDst = pszDst;
4395 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
4396 {
4397 RTUNICP const uc = g_pawcMap[pEntry->achName[offSrc]];
4398 size_t cbCp = RTStrCpSize(uc);
4399 AssertReturnStmt(cbCp < (size_t)(pszDstEnd - pszCurDst), *pszCurDst = '\0', (uint16_t)(pszDstEnd - pszCurDst));
4400 pszCurDst = RTStrPutCp(pszCurDst, uc);
4401 }
4402
4403 /* Extension. */
4404 cchSrc = 3;
4405 while (cchSrc > 0 && RTUniCpIsSpace(g_pawcMap[pEntry->achName[8 + cchSrc - 1]]))
4406 cchSrc--;
4407 if (cchSrc > 0)
4408 {
4409 AssertReturnStmt(1U < (size_t)(pszDstEnd - pszCurDst), *pszCurDst = '\0', (uint16_t)(pszDstEnd - pszCurDst));
4410 *pszCurDst++ = '.';
4411
4412 for (size_t offSrc = 0; offSrc < cchSrc; offSrc++)
4413 {
4414 RTUNICP const uc = g_pawcMap[pEntry->achName[8 + offSrc]];
4415 size_t cbCp = RTStrCpSize(uc);
4416 AssertReturnStmt(cbCp < (size_t)(pszDstEnd - pszCurDst), *pszCurDst = '\0', (uint16_t)(pszDstEnd - pszCurDst));
4417 pszCurDst = RTStrPutCp(pszCurDst, uc);
4418 }
4419 }
4420
4421 *pszCurDst = '\0';
4422 return (uint16_t)(pszDstEnd - pszCurDst);
4423}
4424
4425
4426/**
4427 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
4428 */
4429static DECLCALLBACK(int) rtFsFatDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4430 RTFSOBJATTRADD enmAddAttr)
4431{
4432 PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
4433 PRTFSFATDIRSHRD pShared = pThis->pShared;
4434
4435 /*
4436 * Fake '.' and '..' entries (required for root, we do it everywhere).
4437 */
4438 if (pThis->offDir < 2)
4439 {
4440 size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[pThis->offDir + 2]);
4441 if (cbNeeded < *pcbDirEntry)
4442 *pcbDirEntry = cbNeeded;
4443 else
4444 {
4445 *pcbDirEntry = cbNeeded;
4446 return VERR_BUFFER_OVERFLOW;
4447 }
4448
4449 int rc;
4450 if ( pThis->offDir == 0
4451 || pShared->Core.pParentDir == NULL)
4452 rc = rtFsFatObj_QueryInfo(&pShared->Core, &pDirEntry->Info, enmAddAttr);
4453 else
4454 rc = rtFsFatObj_QueryInfo(&pShared->Core.pParentDir->Core, &pDirEntry->Info, enmAddAttr);
4455
4456 pDirEntry->cwcShortName = 0;
4457 pDirEntry->wszShortName[0] = '\0';
4458 pDirEntry->szName[0] = '.';
4459 pDirEntry->szName[1] = '.';
4460 pDirEntry->szName[++pThis->offDir] = '\0';
4461 pDirEntry->cbName = pThis->offDir;
4462 return rc;
4463 }
4464 if ( pThis->offDir == 2
4465 && pShared->cEntries >= 2)
4466 {
4467 /* Skip '.' and '..' entries if present. */
4468 uint32_t uBufferLock = UINT32_MAX;
4469 uint32_t cEntries = 0;
4470 PCFATDIRENTRYUNION paEntries = NULL;
4471 int rc = rtFsFatDirShrd_GetEntriesAt(pShared, 0, &paEntries, &cEntries, &uBufferLock);
4472 if (RT_FAILURE(rc))
4473 return rc;
4474 if ( (paEntries[0].Entry.fAttrib & FAT_ATTR_DIRECTORY)
4475 && memcmp(paEntries[0].Entry.achName, RT_STR_TUPLE(". ")) == 0)
4476 {
4477 if ( (paEntries[1].Entry.fAttrib & FAT_ATTR_DIRECTORY)
4478 && memcmp(paEntries[1].Entry.achName, RT_STR_TUPLE(".. ")) == 0)
4479 pThis->offDir += sizeof(paEntries[0]) * 2;
4480 else
4481 pThis->offDir += sizeof(paEntries[0]);
4482 }
4483 rtFsFatDirShrd_ReleaseBufferAfterReading(pShared, uBufferLock);
4484 }
4485
4486 /*
4487 * Scan the directory buffer by buffer.
4488 */
4489 RTUTF16 wszName[260+1];
4490 uint8_t bChecksum = UINT8_MAX;
4491 uint8_t idNextSlot = UINT8_MAX;
4492 size_t cwcName = 0;
4493 uint32_t offEntryInDir = pThis->offDir - 2;
4494 uint32_t const cbDir = pShared->Core.cbObject;
4495 Assert(RT_ALIGN_32(cbDir, sizeof(*pDirEntry)) == cbDir);
4496 AssertCompile(FATDIRNAMESLOT_MAX_SLOTS * FATDIRNAMESLOT_CHARS_PER_SLOT < RT_ELEMENTS(wszName));
4497 wszName[260] = '\0';
4498
4499 while (offEntryInDir < cbDir)
4500 {
4501 /* Get chunk of entries starting at offEntryInDir. */
4502 uint32_t uBufferLock = UINT32_MAX;
4503 uint32_t cEntries = 0;
4504 PCFATDIRENTRYUNION paEntries = NULL;
4505 int rc = rtFsFatDirShrd_GetEntriesAt(pShared, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
4506 if (RT_FAILURE(rc))
4507 return rc;
4508
4509 /*
4510 * Now work thru each of the entries.
4511 */
4512 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
4513 {
4514 switch ((uint8_t)paEntries[iEntry].Entry.achName[0])
4515 {
4516 default:
4517 break;
4518 case FATDIRENTRY_CH0_DELETED:
4519 cwcName = 0;
4520 continue;
4521 case FATDIRENTRY_CH0_END_OF_DIR:
4522 if (pShared->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
4523 {
4524 pThis->offDir = cbDir + 2;
4525 rtFsFatDirShrd_ReleaseBufferAfterReading(pShared, uBufferLock);
4526 return VERR_NO_MORE_FILES;
4527 }
4528 cwcName = 0;
4529 break; /* Technically a valid entry before DOS 2.0, or so some claim. */
4530 }
4531
4532 /*
4533 * Check for long filename slot.
4534 */
4535 if ( paEntries[iEntry].Slot.fAttrib == FAT_ATTR_NAME_SLOT
4536 && paEntries[iEntry].Slot.idxZero == 0
4537 && paEntries[iEntry].Slot.fZero == 0
4538 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) <= FATDIRNAMESLOT_HIGHEST_SLOT_ID
4539 && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) != 0)
4540 {
4541 /* New slot? */
4542 if (paEntries[iEntry].Slot.idSlot & FATDIRNAMESLOT_FIRST_SLOT_FLAG)
4543 {
4544 idNextSlot = paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG;
4545 bChecksum = paEntries[iEntry].Slot.bChecksum;
4546 cwcName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
4547 wszName[cwcName] = '\0';
4548 }
4549 /* Is valid next entry? */
4550 else if ( paEntries[iEntry].Slot.idSlot == idNextSlot
4551 && paEntries[iEntry].Slot.bChecksum == bChecksum)
4552 { /* likely */ }
4553 else
4554 cwcName = 0;
4555 if (cwcName)
4556 {
4557 idNextSlot--;
4558 size_t offName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
4559 memcpy(&wszName[offName], paEntries[iEntry].Slot.awcName0, sizeof(paEntries[iEntry].Slot.awcName0));
4560 memcpy(&wszName[offName + 5], paEntries[iEntry].Slot.awcName1, sizeof(paEntries[iEntry].Slot.awcName1));
4561 memcpy(&wszName[offName + 5 + 6], paEntries[iEntry].Slot.awcName2, sizeof(paEntries[iEntry].Slot.awcName2));
4562 }
4563 }
4564 /*
4565 * Got a regular directory entry. Try return it to the caller if not volume label.
4566 */
4567 else if (!(paEntries[iEntry].Entry.fAttrib & FAT_ATTR_VOLUME))
4568 {
4569 /* Do the length calc and check for overflows. */
4570 bool fLongName = false;
4571 size_t cchName = 0;
4572 if ( cwcName != 0
4573 && idNextSlot == 0
4574 && rtFsFatDir_CalcChecksum(&paEntries[iEntry].Entry) == bChecksum)
4575 {
4576 rc = RTUtf16CalcUtf8LenEx(wszName, cwcName, &cchName);
4577 if (RT_SUCCESS(rc))
4578 fLongName = true;
4579 }
4580 if (!fLongName)
4581 cchName = rtFsFatDir_CalcUtf8LengthForDirEntry(pShared, &paEntries[iEntry].Entry);
4582 size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchName + 1]);
4583 if (cbNeeded <= *pcbDirEntry)
4584 *pcbDirEntry = cbNeeded;
4585 else
4586 {
4587 *pcbDirEntry = cbNeeded;
4588 return VERR_BUFFER_OVERFLOW;
4589 }
4590
4591 /* To avoid duplicating code in rtFsFatObj_InitFromDirRec and
4592 rtFsFatObj_QueryInfo, we create a dummy RTFSFATOBJ on the stack. */
4593 RTFSFATOBJ TmpObj;
4594 RT_ZERO(TmpObj);
4595 rtFsFatObj_InitFromDirEntry(&TmpObj, &paEntries[iEntry].Entry, offEntryInDir, pShared->Core.pVol);
4596
4597 rtFsFatDirShrd_ReleaseBufferAfterReading(pShared, uBufferLock);
4598
4599 rc = rtFsFatObj_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
4600
4601 /* Copy out the names. */
4602 pDirEntry->cbName = (uint16_t)cchName;
4603 if (fLongName)
4604 {
4605 char *pszDst = &pDirEntry->szName[0];
4606 int rc2 = RTUtf16ToUtf8Ex(wszName, cwcName, &pszDst, cchName + 1, NULL);
4607 AssertRC(rc2);
4608
4609 pDirEntry->cwcShortName = rtFsFatDir_CopyDirEntryToUtf16(pShared, &paEntries[iEntry].Entry,
4610 pDirEntry->wszShortName,
4611 RT_ELEMENTS(pDirEntry->wszShortName));
4612 }
4613 else
4614 {
4615 rtFsFatDir_CopyDirEntryToUtf8(pShared, &paEntries[iEntry].Entry, &pDirEntry->szName[0], cchName + 1);
4616 pDirEntry->wszShortName[0] = '\0';
4617 pDirEntry->cwcShortName = 0;
4618 }
4619
4620 if (RT_SUCCESS(rc))
4621 pThis->offDir = offEntryInDir + sizeof(paEntries[iEntry]) + 2;
4622 Assert(RTStrValidateEncoding(pDirEntry->szName) == VINF_SUCCESS);
4623 return rc;
4624 }
4625 else
4626 cwcName = 0;
4627 }
4628
4629 rtFsFatDirShrd_ReleaseBufferAfterReading(pShared, uBufferLock);
4630 }
4631
4632 pThis->offDir = cbDir + 2;
4633 return VERR_NO_MORE_FILES;
4634}
4635
4636
4637/**
4638 * FAT directory operations.
4639 */
4640static const RTVFSDIROPS g_rtFsFatDirOps =
4641{
4642 { /* Obj */
4643 RTVFSOBJOPS_VERSION,
4644 RTVFSOBJTYPE_DIR,
4645 "FatDir",
4646 rtFsFatDir_Close,
4647 rtFsFatDir_QueryInfo,
4648 NULL,
4649 RTVFSOBJOPS_VERSION
4650 },
4651 RTVFSDIROPS_VERSION,
4652 0,
4653 { /* ObjSet */
4654 RTVFSOBJSETOPS_VERSION,
4655 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
4656 rtFsFatDir_SetMode,
4657 rtFsFatDir_SetTimes,
4658 rtFsFatDir_SetOwner,
4659 RTVFSOBJSETOPS_VERSION
4660 },
4661 rtFsFatDir_Open,
4662 NULL /* pfnFollowAbsoluteSymlink */,
4663 NULL /* pfnOpenFile*/,
4664 NULL /* pfnOpenDir */,
4665 NULL /* pfnCreateDir */,
4666 rtFsFatDir_OpenSymlink,
4667 rtFsFatDir_CreateSymlink,
4668 NULL /* pfnQueryEntryInfo */,
4669 rtFsFatDir_UnlinkEntry,
4670 rtFsFatDir_RenameEntry,
4671 rtFsFatDir_RewindDir,
4672 rtFsFatDir_ReadDir,
4673 RTVFSDIROPS_VERSION,
4674};
4675
4676
4677
4678
4679/**
4680 * Adds an open child to the parent directory.
4681 *
4682 * Maintains an additional reference to the parent dir to prevent it from going
4683 * away. If @a pDir is the root directory, it also ensures the volume is
4684 * referenced and sticks around until the last open object is gone.
4685 *
4686 * @param pDir The directory.
4687 * @param pChild The child being opened.
4688 * @sa rtFsFatDirShrd_RemoveOpenChild
4689 */
4690static void rtFsFatDirShrd_AddOpenChild(PRTFSFATDIRSHRD pDir, PRTFSFATOBJ pChild)
4691{
4692 rtFsFatDirShrd_Retain(pDir);
4693
4694 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
4695 pChild->pParentDir = pDir;
4696}
4697
4698
4699/**
4700 * Removes an open child to the parent directory.
4701 *
4702 * @param pDir The directory.
4703 * @param pChild The child being removed.
4704 *
4705 * @remarks This is the very last thing you do as it may cause a few other
4706 * objects to be released recursively (parent dir and the volume).
4707 *
4708 * @sa rtFsFatDirShrd_AddOpenChild
4709 */
4710static void rtFsFatDirShrd_RemoveOpenChild(PRTFSFATDIRSHRD pDir, PRTFSFATOBJ pChild)
4711{
4712 AssertReturnVoid(pChild->pParentDir == pDir);
4713 RTListNodeRemove(&pChild->Entry);
4714 pChild->pParentDir = NULL;
4715
4716 rtFsFatDirShrd_Release(pDir);
4717}
4718
4719
4720/**
4721 * Instantiates a new shared directory instance.
4722 *
4723 * @returns IPRT status code.
4724 * @param pThis The FAT volume instance.
4725 * @param pParentDir The parent directory. This is NULL for the root
4726 * directory.
4727 * @param pDirEntry The parent directory entry. This is NULL for the
4728 * root directory.
4729 * @param offEntryInDir The byte offset of the directory entry in the parent
4730 * directory. UINT32_MAX if root directory.
4731 * @param idxCluster The cluster where the directory content is to be
4732 * found. This can be UINT32_MAX if a root FAT12/16
4733 * directory.
4734 * @param offDisk The disk byte offset of the FAT12/16 root directory.
4735 * This is UINT64_MAX if idxCluster is given.
4736 * @param cbDir The size of the directory.
4737 * @param ppSharedDir Where to return shared FAT directory instance.
4738 */
4739static int rtFsFatDirShrd_New(PRTFSFATVOL pThis, PRTFSFATDIRSHRD pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
4740 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTFSFATDIRSHRD *ppSharedDir)
4741{
4742 Assert((idxCluster == UINT32_MAX) != (offDisk == UINT64_MAX));
4743 Assert((pDirEntry == NULL) == (offEntryInDir == UINT32_MAX));
4744 *ppSharedDir = NULL;
4745
4746 int rc = VERR_NO_MEMORY;
4747 PRTFSFATDIRSHRD pShared = (PRTFSFATDIRSHRD)RTMemAllocZ(sizeof(*pShared));
4748 if (pShared)
4749 {
4750 /*
4751 * Initialize it all so rtFsFatDir_Close doesn't trip up in anyway.
4752 */
4753 RTListInit(&pShared->OpenChildren);
4754 if (pDirEntry)
4755 rtFsFatObj_InitFromDirEntry(&pShared->Core, pDirEntry, offEntryInDir, pThis);
4756 else
4757 rtFsFatObj_InitDummy(&pShared->Core, cbDir, RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_UNIX_ALL_PERMS, pThis);
4758
4759 pShared->cEntries = cbDir / sizeof(FATDIRENTRY);
4760 pShared->fIsLinearRootDir = idxCluster == UINT32_MAX;
4761 pShared->fFullyBuffered = pShared->fIsLinearRootDir;
4762 pShared->paEntries = NULL;
4763 pShared->offEntriesOnDisk = UINT64_MAX;
4764 if (pShared->fFullyBuffered)
4765 pShared->cbAllocatedForEntries = RT_ALIGN_32(cbDir, pThis->cbSector);
4766 else
4767 pShared->cbAllocatedForEntries = pThis->cbSector;
4768
4769 /*
4770 * If clustered backing, read the chain and see if we cannot still do the full buffering.
4771 */
4772 if (idxCluster != UINT32_MAX)
4773 {
4774 rc = rtFsFatClusterMap_ReadClusterChain(pThis, idxCluster, &pShared->Core.Clusters);
4775 if (RT_SUCCESS(rc))
4776 {
4777 if ( pShared->Core.Clusters.cClusters >= 1
4778 && pShared->Core.Clusters.cbChain <= _64K
4779 && rtFsFatChain_IsContiguous(&pShared->Core.Clusters))
4780 {
4781 Assert(pShared->Core.Clusters.cbChain >= cbDir);
4782 pShared->cbAllocatedForEntries = pShared->Core.Clusters.cbChain;
4783 pShared->fFullyBuffered = true;
4784 }
4785
4786 /* DOS doesn't set a size on directores, so use the cluster length instead. */
4787 if ( cbDir == 0
4788 && pShared->Core.Clusters.cbChain > 0)
4789 {
4790 cbDir = pShared->Core.Clusters.cbChain;
4791 pShared->Core.cbObject = cbDir;
4792 pShared->cEntries = cbDir / sizeof(FATDIRENTRY);
4793 if (pShared->fFullyBuffered)
4794 pShared->cbAllocatedForEntries = RT_ALIGN_32(cbDir, pThis->cbSector);
4795 }
4796 }
4797 }
4798 else
4799 {
4800 rtFsFatChain_InitEmpty(&pShared->Core.Clusters, pThis);
4801 rc = VINF_SUCCESS;
4802 }
4803 if (RT_SUCCESS(rc))
4804 {
4805 /*
4806 * Allocate and initialize the buffering. Fill the buffer.
4807 */
4808 pShared->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pShared->cbAllocatedForEntries);
4809 if (!pShared->paEntries)
4810 {
4811 if (pShared->fFullyBuffered && !pShared->fIsLinearRootDir)
4812 {
4813 pShared->fFullyBuffered = false;
4814 pShared->cbAllocatedForEntries = pThis->cbSector;
4815 pShared->paEntries = (PFATDIRENTRYUNION)RTMemAlloc(pShared->cbAllocatedForEntries);
4816 }
4817 if (!pShared->paEntries)
4818 rc = VERR_NO_MEMORY;
4819 }
4820
4821 if (RT_SUCCESS(rc))
4822 {
4823 if (pShared->fFullyBuffered)
4824 {
4825 pShared->u.Full.cDirtySectors = 0;
4826 pShared->u.Full.cSectors = pShared->cbAllocatedForEntries / pThis->cbSector;
4827 pShared->u.Full.pbDirtySectors = (uint8_t *)RTMemAllocZ((pShared->u.Full.cSectors + 63) / 8);
4828 if (pShared->u.Full.pbDirtySectors)
4829 pShared->offEntriesOnDisk = offDisk != UINT64_MAX ? offDisk
4830 : rtFsFatClusterToDiskOffset(pThis, idxCluster);
4831 else
4832 rc = VERR_NO_MEMORY;
4833 }
4834 else
4835 {
4836 pShared->offEntriesOnDisk = rtFsFatClusterToDiskOffset(pThis, idxCluster);
4837 pShared->u.Simple.offInDir = 0;
4838 pShared->u.Simple.fDirty = false;
4839 }
4840 if (RT_SUCCESS(rc))
4841 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->offEntriesOnDisk,
4842 pShared->paEntries, pShared->cbAllocatedForEntries, NULL);
4843 if (RT_SUCCESS(rc))
4844 {
4845 /*
4846 * Link into parent directory so we can use it to update
4847 * our directory entry.
4848 */
4849 if (pParentDir)
4850 rtFsFatDirShrd_AddOpenChild(pParentDir, &pShared->Core);
4851 *ppSharedDir = pShared;
4852 return VINF_SUCCESS;
4853 }
4854 }
4855
4856 /* Free the buffer on failure so rtFsFatDir_Close doesn't try do anything with it. */
4857 RTMemFree(pShared->paEntries);
4858 pShared->paEntries = NULL;
4859 }
4860
4861 Assert(pShared->Core.cRefs == 1);
4862 rtFsFatDirShrd_Release(pShared);
4863 }
4864 return rc;
4865}
4866
4867
4868/**
4869 * Instantiates a new directory with a shared structure presupplied.
4870 *
4871 * @returns IPRT status code.
4872 * @param pThis The FAT volume instance.
4873 * @param pShared Referenced pointer to the shared structure. The
4874 * reference is always CONSUMED.
4875 * @param phVfsDir Where to return the directory handle.
4876 */
4877static int rtFsFatDir_NewWithShared(PRTFSFATVOL pThis, PRTFSFATDIRSHRD pShared, PRTVFSDIR phVfsDir)
4878{
4879 /*
4880 * Create VFS object around the shared structure.
4881 */
4882 PRTFSFATDIR pNewDir;
4883 int rc = RTVfsNewDir(&g_rtFsFatDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
4884 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
4885 if (RT_SUCCESS(rc))
4886 {
4887 /*
4888 * Look for existing shared object, create a new one if necessary.
4889 * We CONSUME a reference to pShared here.
4890 */
4891 pNewDir->offDir = 0;
4892 pNewDir->pShared = pShared;
4893 return VINF_SUCCESS;
4894 }
4895
4896 rtFsFatDirShrd_Release(pShared);
4897 *phVfsDir = NIL_RTVFSDIR;
4898 return rc;
4899}
4900
4901
4902
4903/**
4904 * Instantiates a new directory VFS, creating the shared structure as necessary.
4905 *
4906 * @returns IPRT status code.
4907 * @param pThis The FAT volume instance.
4908 * @param pParentDir The parent directory. This is NULL for the root
4909 * directory.
4910 * @param pDirEntry The parent directory entry. This is NULL for the
4911 * root directory.
4912 * @param offEntryInDir The byte offset of the directory entry in the parent
4913 * directory. UINT32_MAX if root directory.
4914 * @param idxCluster The cluster where the directory content is to be
4915 * found. This can be UINT32_MAX if a root FAT12/16
4916 * directory.
4917 * @param offDisk The disk byte offset of the FAT12/16 root directory.
4918 * This is UINT64_MAX if idxCluster is given.
4919 * @param cbDir The size of the directory.
4920 * @param phVfsDir Where to return the directory handle.
4921 */
4922static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIRSHRD pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
4923 uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir)
4924{
4925 /*
4926 * Look for existing shared object, create a new one if necessary.
4927 */
4928 PRTFSFATDIRSHRD pShared = (PRTFSFATDIRSHRD)rtFsFatDirShrd_LookupShared(pParentDir, offEntryInDir);
4929 if (!pShared)
4930 {
4931 int rc = rtFsFatDirShrd_New(pThis, pParentDir, pDirEntry, offEntryInDir, idxCluster, offDisk, cbDir, &pShared);
4932 if (RT_FAILURE(rc))
4933 {
4934 *phVfsDir = NIL_RTVFSDIR;
4935 return rc;
4936 }
4937 }
4938 return rtFsFatDir_NewWithShared(pThis, pShared, phVfsDir);
4939}
4940
4941
4942
4943
4944
4945/**
4946 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
4947 */
4948static DECLCALLBACK(int) rtFsFatVol_Close(void *pvThis)
4949{
4950 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
4951 LogFlow(("rtFsFatVol_Close(%p)\n", pThis));
4952
4953 int rc = VINF_SUCCESS;
4954 if (pThis->pRootDir != NULL)
4955 {
4956 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
4957 Assert(pThis->pRootDir->Core.cRefs == 1);
4958 rc = rtFsFatDirShrd_Release(pThis->pRootDir);
4959 pThis->pRootDir = NULL;
4960 }
4961
4962 int rc2 = rtFsFatClusterMap_Destroy(pThis);
4963 if (RT_SUCCESS(rc))
4964 rc = rc2;
4965
4966 RTVfsFileRelease(pThis->hVfsBacking);
4967 pThis->hVfsBacking = NIL_RTVFSFILE;
4968
4969 return rc;
4970}
4971
4972
4973/**
4974 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
4975 */
4976static DECLCALLBACK(int) rtFsFatVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4977{
4978 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
4979 return VERR_WRONG_TYPE;
4980}
4981
4982
4983/**
4984 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
4985 */
4986static DECLCALLBACK(int) rtFsFatVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
4987{
4988 PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
4989
4990 rtFsFatDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
4991 return rtFsFatDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
4992}
4993
4994
4995/**
4996 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
4997 */
4998static DECLCALLBACK(int) rtFsFatVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
4999{
5000
5001
5002 RT_NOREF(pvThis, off, cb, pfUsed);
5003 return VERR_NOT_IMPLEMENTED;
5004}
5005
5006
5007DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsFatVolOps =
5008{
5009 { /* Obj */
5010 RTVFSOBJOPS_VERSION,
5011 RTVFSOBJTYPE_VFS,
5012 "FatVol",
5013 rtFsFatVol_Close,
5014 rtFsFatVol_QueryInfo,
5015 NULL,
5016 RTVFSOBJOPS_VERSION
5017 },
5018 RTVFSOPS_VERSION,
5019 0 /* fFeatures */,
5020 rtFsFatVol_OpenRoot,
5021 rtFsFatVol_QueryRangeState,
5022 RTVFSOPS_VERSION
5023};
5024
5025
5026/**
5027 * Tries to detect a DOS 1.x formatted image and fills in the BPB fields.
5028 *
5029 * There is no BPB here, but fortunately, there isn't much variety.
5030 *
5031 * @returns IPRT status code.
5032 * @param pThis The FAT volume instance, BPB derived fields are filled
5033 * in on success.
5034 * @param pBootSector The boot sector.
5035 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
5036 * the boot sector.
5037 * @param pErrInfo Where to return additional error information.
5038 */
5039static int rtFsFatVolTryInitDos1x(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t const *pbFatSector,
5040 PRTERRINFO pErrInfo)
5041{
5042 /*
5043 * PC-DOS 1.0 does a 2fh byte short jump w/o any NOP following it.
5044 * Instead the following are three words and a 9 byte build date
5045 * string. The remaining space is zero filled.
5046 *
5047 * Note! No idea how this would look like for 8" floppies, only got 5"1/4'.
5048 *
5049 * ASSUME all non-BPB disks are using this format.
5050 */
5051 if ( pBootSector->abJmp[0] != 0xeb /* jmp rel8 */
5052 || pBootSector->abJmp[1] < 0x2f
5053 || pBootSector->abJmp[1] >= 0x80
5054 || pBootSector->abJmp[2] == 0x90 /* nop */)
5055 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5056 "No DOS v1.0 bootsector either - invalid jmp: %.3Rhxs", pBootSector->abJmp);
5057 uint32_t const offJump = 2 + pBootSector->abJmp[1];
5058 uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
5059 Assert(offFirstZero >= RT_UOFFSETOF(FATBOOTSECTOR, Bpb));
5060 uint32_t const cbZeroPad = RT_MIN(offJump - offFirstZero,
5061 sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_UOFFSETOF(FATBOOTSECTOR, Bpb)));
5062
5063 if (!ASMMemIsAllU8((uint8_t const *)pBootSector + offFirstZero, cbZeroPad, 0))
5064 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5065 "No DOS v1.0 bootsector either - expected zero padding %#x LB %#x: %.*Rhxs",
5066 offFirstZero, cbZeroPad, cbZeroPad, (uint8_t const *)pBootSector + offFirstZero);
5067
5068 /*
5069 * Check the FAT ID so we can tell if this is double or single sided,
5070 * as well as being a valid FAT12 start.
5071 */
5072 if ( (pbFatSector[0] != 0xfe && pbFatSector[0] != 0xff)
5073 || pbFatSector[1] != 0xff
5074 || pbFatSector[2] != 0xff)
5075 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5076 "No DOS v1.0 bootsector either - unexpected start of FAT: %.3Rhxs", pbFatSector);
5077
5078 /*
5079 * Fixed DOS 1.0 config.
5080 */
5081 pThis->enmFatType = RTFSFATTYPE_FAT12;
5082 pThis->enmBpbVersion = RTFSFATBPBVER_NO_BPB;
5083 pThis->bMedia = pbFatSector[0];
5084 pThis->cReservedSectors = 1;
5085 pThis->cbSector = 512;
5086 pThis->cbCluster = pThis->bMedia == 0xfe ? 1024 : 512;
5087 pThis->cFats = 2;
5088 pThis->cbFat = 512;
5089 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * 512;
5090 pThis->aoffFats[1] = pThis->aoffFats[0] + pThis->cbFat;
5091 pThis->offRootDir = pThis->aoffFats[1] + pThis->cbFat;
5092 pThis->cRootDirEntries = 512;
5093 pThis->offFirstCluster = pThis->offRootDir + RT_ALIGN_32(pThis->cRootDirEntries * sizeof(FATDIRENTRY),
5094 pThis->cbSector);
5095 pThis->cbTotalSize = pThis->bMedia == 0xfe ? 8 * 1 * 40 * 512 : 8 * 2 * 40 * 512;
5096 pThis->cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
5097 return VINF_SUCCESS;
5098}
5099
5100
5101/**
5102 * Worker for rtFsFatVolTryInitDos2Plus that handles remaining BPB fields.
5103 *
5104 * @returns IPRT status code.
5105 * @param pThis The FAT volume instance, BPB derived fields are filled
5106 * in on success.
5107 * @param pBootSector The boot sector.
5108 * @param fMaybe331 Set if it could be a DOS v3.31 BPB.
5109 * @param pErrInfo Where to return additional error information.
5110 */
5111static int rtFsFatVolTryInitDos2PlusBpb(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, bool fMaybe331, PRTERRINFO pErrInfo)
5112{
5113 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_2_0;
5114
5115 /*
5116 * Figure total sector count. Could both be zero, in which case we have to
5117 * fall back on the size of the backing stuff.
5118 */
5119 if (pBootSector->Bpb.Bpb20.cTotalSectors16 != 0)
5120 pThis->cbTotalSize = pBootSector->Bpb.Bpb20.cTotalSectors16 * pThis->cbSector;
5121 else if ( pBootSector->Bpb.Bpb331.cTotalSectors32 != 0
5122 && fMaybe331)
5123 {
5124 pThis->enmBpbVersion = RTFSFATBPBVER_DOS_3_31;
5125 pThis->cbTotalSize = pBootSector->Bpb.Bpb331.cTotalSectors32 * (uint64_t)pThis->cbSector;
5126 }
5127 else
5128 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
5129 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
5130 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5131 "Bogus FAT12/16 total or reserved sector count: %#x vs %#x",
5132 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
5133
5134 /*
5135 * The fat size. Complete FAT offsets.
5136 */
5137 if ( pBootSector->Bpb.Bpb20.cSectorsPerFat == 0
5138 || ((uint32_t)pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cFats + 1) * pThis->cbSector > pThis->cbTotalSize)
5139 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 sectors per FAT: %#x (total sectors %#RX64)",
5140 pBootSector->Bpb.Bpb20.cSectorsPerFat, pThis->cbTotalSize / pThis->cbSector);
5141 pThis->cbFat = pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cbSector;
5142
5143 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
5144 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
5145 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
5146
5147 /*
5148 * Do root directory calculations.
5149 */
5150 pThis->idxRootDirCluster = UINT32_MAX;
5151 pThis->offRootDir = pThis->aoffFats[pThis->cFats];
5152 if (pThis->cRootDirEntries == 0)
5153 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero FAT12/16 root directory size");
5154 pThis->cbRootDir = pThis->cRootDirEntries * sizeof(FATDIRENTRY);
5155 pThis->cbRootDir = RT_ALIGN_32(pThis->cbRootDir, pThis->cbSector);
5156
5157 /*
5158 * First cluster and cluster count checks and calcs. Determin FAT type.
5159 */
5160 pThis->offFirstCluster = pThis->offRootDir + pThis->cbRootDir;
5161 uint64_t cbSystemStuff = pThis->offFirstCluster - pThis->offBootSector;
5162 if (cbSystemStuff >= pThis->cbTotalSize)
5163 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 total size, root dir, or fat size");
5164 pThis->cClusters = (pThis->cbTotalSize - cbSystemStuff) / pThis->cbCluster;
5165
5166 if (pThis->cClusters >= FAT_MAX_FAT16_DATA_CLUSTERS)
5167 {
5168 pThis->cClusters = FAT_MAX_FAT16_DATA_CLUSTERS;
5169 pThis->enmFatType = RTFSFATTYPE_FAT16;
5170 }
5171 else if (pThis->cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS)
5172 pThis->enmFatType = RTFSFATTYPE_FAT16;
5173 else
5174 pThis->enmFatType = RTFSFATTYPE_FAT12; /** @todo Not sure if this is entirely the right way to go about it... */
5175
5176 uint32_t cClustersPerFat;
5177 if (pThis->enmFatType == RTFSFATTYPE_FAT16)
5178 cClustersPerFat = pThis->cbFat / 2;
5179 else
5180 cClustersPerFat = pThis->cbFat * 2 / 3;
5181 if (pThis->cClusters > cClustersPerFat)
5182 pThis->cClusters = cClustersPerFat;
5183
5184 return VINF_SUCCESS;
5185}
5186
5187
5188/**
5189 * Worker for rtFsFatVolTryInitDos2Plus and rtFsFatVolTryInitDos2PlusFat32 that
5190 * handles common extended BPBs fields.
5191 *
5192 * @returns IPRT status code.
5193 * @param pThis The FAT volume instance.
5194 * @param bExtSignature The extended BPB signature.
5195 * @param uSerialNumber The serial number.
5196 * @param pachLabel Pointer to the volume label field.
5197 * @param pachType Pointer to the file system type field.
5198 */
5199static void rtFsFatVolInitCommonEbpbBits(PRTFSFATVOL pThis, uint8_t bExtSignature, uint32_t uSerialNumber,
5200 char const *pachLabel, char const *pachType)
5201{
5202 pThis->uSerialNo = uSerialNumber;
5203 if (bExtSignature == FATEBPB_SIGNATURE)
5204 {
5205 memcpy(pThis->szLabel, pachLabel, RT_SIZEOFMEMB(FATEBPB, achLabel));
5206 pThis->szLabel[RT_SIZEOFMEMB(FATEBPB, achLabel)] = '\0';
5207 RTStrStrip(pThis->szLabel);
5208
5209 memcpy(pThis->szType, pachType, RT_SIZEOFMEMB(FATEBPB, achType));
5210 pThis->szType[RT_SIZEOFMEMB(FATEBPB, achType)] = '\0';
5211 RTStrStrip(pThis->szType);
5212 }
5213 else
5214 {
5215 pThis->szLabel[0] = '\0';
5216 pThis->szType[0] = '\0';
5217 }
5218}
5219
5220
5221/**
5222 * Worker for rtFsFatVolTryInitDos2Plus that deals with FAT32.
5223 *
5224 * @returns IPRT status code.
5225 * @param pThis The FAT volume instance, BPB derived fields are filled
5226 * in on success.
5227 * @param pBootSector The boot sector.
5228 * @param pErrInfo Where to return additional error information.
5229 */
5230static int rtFsFatVolTryInitDos2PlusFat32(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, PRTERRINFO pErrInfo)
5231{
5232 pThis->enmFatType = RTFSFATTYPE_FAT32;
5233 pThis->enmBpbVersion = pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE
5234 ? RTFSFATBPBVER_FAT32_29 : RTFSFATBPBVER_FAT32_28;
5235 pThis->fFat32Flags = pBootSector->Bpb.Fat32Ebpb.fFlags;
5236
5237 if (pBootSector->Bpb.Fat32Ebpb.uVersion != FAT32EBPB_VERSION_0_0)
5238 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Unsupported FAT32 version: %d.%d (%#x)",
5239 RT_HI_U8(pBootSector->Bpb.Fat32Ebpb.uVersion), RT_LO_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),
5240 pBootSector->Bpb.Fat32Ebpb.uVersion);
5241
5242 /*
5243 * Figure total sector count. We expected it to be filled in.
5244 */
5245 bool fUsing64BitTotalSectorCount = false;
5246 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 != 0)
5247 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 * pThis->cbSector;
5248 else if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 != 0)
5249 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 * (uint64_t)pThis->cbSector;
5250 else if ( pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 <= UINT64_MAX / 512
5251 && pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 > 3
5252 && pBootSector->Bpb.Fat32Ebpb.bExtSignature != FATEBPB_SIGNATURE_OLD)
5253 {
5254 pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 * pThis->cbSector;
5255 fUsing64BitTotalSectorCount = true;
5256 }
5257 else
5258 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 total sector count out of range: %#RX64",
5259 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64);
5260 if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
5261 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5262 "Bogus FAT32 total or reserved sector count: %#x vs %#x",
5263 pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
5264
5265 /*
5266 * Fat size. We check the 16-bit field even if it probably should be zero all the time.
5267 */
5268 if (pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat != 0)
5269 {
5270 if ( pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != 0
5271 && pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat)
5272 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5273 "Both 16-bit and 32-bit FAT size fields are set: %#RX16 vs %#RX32",
5274 pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat, pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
5275 pThis->cbFat = pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat * pThis->cbSector;
5276 }
5277 else
5278 {
5279 uint64_t cbFat = pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 * (uint64_t)pThis->cbSector;
5280 if ( cbFat == 0
5281 || cbFat >= FAT_MAX_FAT32_TOTAL_CLUSTERS * 4 + pThis->cbSector * 16)
5282 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5283 "Bogus 32-bit FAT size: %#RX32", pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
5284 pThis->cbFat = (uint32_t)cbFat;
5285 }
5286
5287 /*
5288 * Complete the FAT offsets and first cluster offset, then calculate number
5289 * of data clusters.
5290 */
5291 AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
5292 for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
5293 pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
5294 pThis->offFirstCluster = pThis->aoffFats[pThis->cFats];
5295
5296 if (pThis->offFirstCluster - pThis->offBootSector >= pThis->cbTotalSize)
5297 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5298 "Bogus 32-bit FAT size or total sector count: cFats=%d cbFat=%#x cbTotalSize=%#x",
5299 pThis->cFats, pThis->cbFat, pThis->cbTotalSize);
5300
5301 uint64_t cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
5302 if (cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS)
5303 pThis->cClusters = (uint32_t)cClusters;
5304 else
5305 pThis->cClusters = FAT_MAX_FAT32_DATA_CLUSTERS;
5306 if (pThis->cClusters > (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER))
5307 pThis->cClusters = (pThis->cbFat / 4 - FAT_FIRST_DATA_CLUSTER);
5308
5309 /*
5310 * Root dir cluster.
5311 */
5312 if ( pBootSector->Bpb.Fat32Ebpb.uRootDirCluster < FAT_FIRST_DATA_CLUSTER
5313 || pBootSector->Bpb.Fat32Ebpb.uRootDirCluster >= pThis->cClusters)
5314 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5315 "Bogus FAT32 root directory cluster: %#x", pBootSector->Bpb.Fat32Ebpb.uRootDirCluster);
5316 pThis->idxRootDirCluster = pBootSector->Bpb.Fat32Ebpb.uRootDirCluster;
5317 pThis->offRootDir = pThis->offFirstCluster
5318 + (pBootSector->Bpb.Fat32Ebpb.uRootDirCluster - FAT_FIRST_DATA_CLUSTER) * pThis->cbCluster;
5319
5320 /*
5321 * Info sector.
5322 */
5323 if ( pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == 0
5324 || pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == UINT16_MAX)
5325 pThis->offFat32InfoSector = UINT64_MAX;
5326 else if (pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo >= pThis->cReservedSectors)
5327 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5328 "Bogus FAT32 info sector number: %#x (reserved sectors %#x)",
5329 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pThis->cReservedSectors);
5330 else
5331 {
5332 pThis->offFat32InfoSector = pThis->cbSector * pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo + pThis->offBootSector;
5333 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->offFat32InfoSector,
5334 &pThis->Fat32InfoSector, sizeof(pThis->Fat32InfoSector), NULL);
5335 if (RT_FAILURE(rc))
5336 return RTErrInfoSetF(pErrInfo, rc, "Failed to read FAT32 info sector at offset %#RX64", pThis->offFat32InfoSector);
5337 if ( pThis->Fat32InfoSector.uSignature1 != FAT32INFOSECTOR_SIGNATURE_1
5338 || pThis->Fat32InfoSector.uSignature2 != FAT32INFOSECTOR_SIGNATURE_2
5339 || pThis->Fat32InfoSector.uSignature3 != FAT32INFOSECTOR_SIGNATURE_3)
5340 return RTErrInfoSetF(pErrInfo, rc, "FAT32 info sector signature mismatch: %#x %#x %#x",
5341 pThis->Fat32InfoSector.uSignature1, pThis->Fat32InfoSector.uSignature2,
5342 pThis->Fat32InfoSector.uSignature3);
5343 }
5344
5345 /*
5346 * Boot sector copy.
5347 */
5348 if ( pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == 0
5349 || pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == UINT16_MAX)
5350 {
5351 pThis->cBootSectorCopies = 0;
5352 pThis->offBootSectorCopies = UINT64_MAX;
5353 }
5354 else if (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo >= pThis->cReservedSectors)
5355 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5356 "Bogus FAT32 info boot sector copy location: %#x (reserved sectors %#x)",
5357 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo, pThis->cReservedSectors);
5358 else
5359 {
5360 /** @todo not sure if cbSector is correct here. */
5361 pThis->cBootSectorCopies = 3;
5362 if ( (uint32_t)pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo + pThis->cBootSectorCopies
5363 > pThis->cReservedSectors)
5364 pThis->cBootSectorCopies = (uint8_t)(pThis->cReservedSectors - pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
5365 pThis->offBootSectorCopies = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo * pThis->cbSector + pThis->offBootSector;
5366 if ( pThis->offFat32InfoSector != UINT64_MAX
5367 && pThis->offFat32InfoSector - pThis->offBootSectorCopies < (uint64_t)(pThis->cBootSectorCopies * pThis->cbSector))
5368 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 info sector and boot sector copies overlap: %#x vs %#x",
5369 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
5370 }
5371
5372 /*
5373 * Serial number, label and type.
5374 */
5375 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Fat32Ebpb.bExtSignature, pBootSector->Bpb.Fat32Ebpb.uSerialNumber,
5376 pBootSector->Bpb.Fat32Ebpb.achLabel,
5377 fUsing64BitTotalSectorCount ? pBootSector->achOemName : pBootSector->Bpb.Fat32Ebpb.achLabel);
5378 if (pThis->szType[0] == '\0')
5379 memcpy(pThis->szType, "FAT32", 6);
5380
5381 return VINF_SUCCESS;
5382}
5383
5384
5385/**
5386 * Tries to detect a DOS 2.0+ formatted image and fills in the BPB fields.
5387 *
5388 * We ASSUME BPB here, but need to figure out which version of the BPB it is,
5389 * which is lots of fun.
5390 *
5391 * @returns IPRT status code.
5392 * @param pThis The FAT volume instance, BPB derived fields are filled
5393 * in on success.
5394 * @param pBootSector The boot sector.
5395 * @param pbFatSector Points to the FAT sector, or whatever is 512 bytes after
5396 * the boot sector. On successful return it will contain
5397 * the first FAT sector.
5398 * @param pErrInfo Where to return additional error information.
5399 */
5400static int rtFsFatVolTryInitDos2Plus(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t *pbFatSector, PRTERRINFO pErrInfo)
5401{
5402 /*
5403 * Check if we've got a known jump instruction first, because that will
5404 * give us a max (E)BPB size hint.
5405 */
5406 uint8_t offJmp = UINT8_MAX;
5407 if ( pBootSector->abJmp[0] == 0xeb
5408 && pBootSector->abJmp[1] <= 0x7f)
5409 offJmp = pBootSector->abJmp[1] + 2;
5410 else if ( pBootSector->abJmp[0] == 0x90
5411 && pBootSector->abJmp[1] == 0xeb
5412 && pBootSector->abJmp[2] <= 0x7f)
5413 offJmp = pBootSector->abJmp[2] + 3;
5414 else if ( pBootSector->abJmp[0] == 0xe9
5415 && pBootSector->abJmp[2] <= 0x7f)
5416 offJmp = RT_MIN(127, RT_MAKE_U16(pBootSector->abJmp[1], pBootSector->abJmp[2]));
5417 uint8_t const cbMaxBpb = offJmp - RT_UOFFSETOF(FATBOOTSECTOR, Bpb);
5418
5419 /*
5420 * Do the basic DOS v2.0 BPB fields.
5421 */
5422 if (cbMaxBpb < sizeof(FATBPB20))
5423 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5424 "DOS signature, but jmp too short for any BPB: %#x (max %#x BPB)", offJmp, cbMaxBpb);
5425
5426 if (pBootSector->Bpb.Bpb20.cFats == 0)
5427 return RTErrInfoSet(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, number of FATs is zero, so not FAT file system");
5428 if (pBootSector->Bpb.Bpb20.cFats > 4)
5429 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many FATs: %#x", pBootSector->Bpb.Bpb20.cFats);
5430 pThis->cFats = pBootSector->Bpb.Bpb20.cFats;
5431
5432 if (!FATBPB_MEDIA_IS_VALID(pBootSector->Bpb.Bpb20.bMedia))
5433 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5434 "DOS signature, invalid media byte: %#x", pBootSector->Bpb.Bpb20.bMedia);
5435 pThis->bMedia = pBootSector->Bpb.Bpb20.bMedia;
5436
5437 if (!RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cbSector))
5438 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5439 "DOS signature, sector size not power of two: %#x", pBootSector->Bpb.Bpb20.cbSector);
5440 if ( pBootSector->Bpb.Bpb20.cbSector != 512
5441 && pBootSector->Bpb.Bpb20.cbSector != 4096
5442 && pBootSector->Bpb.Bpb20.cbSector != 1024
5443 && pBootSector->Bpb.Bpb20.cbSector != 128)
5444 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5445 "DOS signature, unsupported sector size: %#x", pBootSector->Bpb.Bpb20.cbSector);
5446 pThis->cbSector = pBootSector->Bpb.Bpb20.cbSector;
5447
5448 if ( !RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cSectorsPerCluster)
5449 || !pBootSector->Bpb.Bpb20.cSectorsPerCluster)
5450 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, cluster size not non-zero power of two: %#x",
5451 pBootSector->Bpb.Bpb20.cSectorsPerCluster);
5452 pThis->cbCluster = pBootSector->Bpb.Bpb20.cSectorsPerCluster * pThis->cbSector;
5453
5454 uint64_t const cMaxRoot = (pThis->cbBacking - pThis->offBootSector - 512) / sizeof(FATDIRENTRY); /* we'll check again later. */
5455 if (pBootSector->Bpb.Bpb20.cMaxRootDirEntries >= cMaxRoot)
5456 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many root entries: %#x (max %#RX64)",
5457 pBootSector->Bpb.Bpb20.cSectorsPerCluster, cMaxRoot);
5458 pThis->cRootDirEntries = pBootSector->Bpb.Bpb20.cMaxRootDirEntries;
5459
5460 if ( pBootSector->Bpb.Bpb20.cReservedSectors == 0
5461 || pBootSector->Bpb.Bpb20.cReservedSectors >= _32K)
5462 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5463 "DOS signature, bogus reserved sector count: %#x", pBootSector->Bpb.Bpb20.cReservedSectors);
5464 pThis->cReservedSectors = pBootSector->Bpb.Bpb20.cReservedSectors;
5465 pThis->aoffFats[0] = pThis->offBootSector + pThis->cReservedSectors * pThis->cbSector;
5466
5467 /*
5468 * Jump ahead and check for FAT32 EBPB.
5469 * If found, we simply ASSUME it's a FAT32 file system.
5470 */
5471 int rc;
5472 if ( ( sizeof(FAT32EBPB) <= cbMaxBpb
5473 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE)
5474 || ( RT_UOFFSETOF(FAT32EBPB, achLabel) <= cbMaxBpb
5475 && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
5476 {
5477 rc = rtFsFatVolTryInitDos2PlusFat32(pThis, pBootSector, pErrInfo);
5478 if (RT_FAILURE(rc))
5479 return rc;
5480 }
5481 else
5482 {
5483 /*
5484 * Check for extended BPB, otherwise we'll have to make qualified guesses
5485 * about what kind of BPB we're up against based on jmp offset and zero fields.
5486 * ASSUMES either FAT16 or FAT12.
5487 */
5488 if ( ( sizeof(FATEBPB) <= cbMaxBpb
5489 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE)
5490 || ( RT_UOFFSETOF(FATEBPB, achLabel) <= cbMaxBpb
5491 && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
5492 {
5493 rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Ebpb.bExtSignature, pBootSector->Bpb.Ebpb.uSerialNumber,
5494 pBootSector->Bpb.Ebpb.achLabel, pBootSector->Bpb.Ebpb.achType);
5495 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, true /*fMaybe331*/, pErrInfo);
5496 pThis->enmBpbVersion = pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE
5497 ? RTFSFATBPBVER_EXT_29 : RTFSFATBPBVER_EXT_28;
5498 }
5499 else
5500 rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, cbMaxBpb >= sizeof(FATBPB331), pErrInfo);
5501 if (RT_FAILURE(rc))
5502 return rc;
5503 if (pThis->szType[0] == '\0')
5504 memcpy(pThis->szType, pThis->enmFatType == RTFSFATTYPE_FAT12 ? "FAT12" : "FAT16", 6);
5505 }
5506
5507 /*
5508 * Check the FAT ID. May have to read a bit of the FAT into the buffer.
5509 */
5510 if (pThis->aoffFats[0] != pThis->offBootSector + 512)
5511 {
5512 rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0], pbFatSector, 512, NULL);
5513 if (RT_FAILURE(rc))
5514 return RTErrInfoSet(pErrInfo, rc, "error reading first FAT sector");
5515 }
5516 if (pbFatSector[0] != pThis->bMedia)
5517 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5518 "Media byte and FAT ID mismatch: %#x vs %#x (%.7Rhxs)", pbFatSector[0], pThis->bMedia, pbFatSector);
5519 uint32_t idxOurEndOfChain;
5520 switch (pThis->enmFatType)
5521 {
5522 case RTFSFATTYPE_FAT12:
5523 if ((pbFatSector[1] & 0xf) != 0xf)
5524 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT12): %.3Rhxs", pbFatSector);
5525 pThis->idxMaxLastCluster = FAT_LAST_FAT12_DATA_CLUSTER;
5526 pThis->idxEndOfChain = (pbFatSector[1] >> 4) | ((uint32_t)pbFatSector[2] << 4);
5527 idxOurEndOfChain = FAT_FIRST_FAT12_EOC | 0xf;
5528 break;
5529
5530 case RTFSFATTYPE_FAT16:
5531 if (pbFatSector[1] != 0xff)
5532 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT16): %.4Rhxs", pbFatSector);
5533 pThis->idxMaxLastCluster = FAT_LAST_FAT16_DATA_CLUSTER;
5534 pThis->idxEndOfChain = RT_MAKE_U16(pbFatSector[2], pbFatSector[3]);
5535 idxOurEndOfChain = FAT_FIRST_FAT16_EOC | 0xf;
5536 break;
5537
5538 case RTFSFATTYPE_FAT32:
5539 if ( pbFatSector[1] != 0xff
5540 || pbFatSector[2] != 0xff
5541 || (pbFatSector[3] & 0x0f) != 0x0f)
5542 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT32): %.8Rhxs", pbFatSector);
5543 pThis->idxMaxLastCluster = FAT_LAST_FAT32_DATA_CLUSTER;
5544 pThis->idxEndOfChain = RT_MAKE_U32_FROM_U8(pbFatSector[4], pbFatSector[5], pbFatSector[6], pbFatSector[7]);
5545 idxOurEndOfChain = FAT_FIRST_FAT32_EOC | 0xf;
5546 break;
5547
5548 default: AssertFailedReturn(VERR_INTERNAL_ERROR_2);
5549 }
5550
5551 if (pThis->idxEndOfChain <= pThis->idxMaxLastCluster)
5552 {
5553 Log(("rtFsFatVolTryInitDos2Plus: Bogus idxEndOfChain=%#x, using %#x instead\n", pThis->idxEndOfChain, idxOurEndOfChain));
5554 pThis->idxEndOfChain = idxOurEndOfChain;
5555 }
5556
5557 RT_NOREF(pbFatSector);
5558 return VINF_SUCCESS;
5559}
5560
5561
5562/**
5563 * Given a power of two value @a cb return exponent value.
5564 *
5565 * @returns Shift count
5566 * @param cb The value.
5567 */
5568static uint8_t rtFsFatVolCalcByteShiftCount(uint32_t cb)
5569{
5570 Assert(RT_IS_POWER_OF_TWO(cb));
5571 unsigned iBit = ASMBitFirstSetU32(cb);
5572 Assert(iBit >= 1);
5573 iBit--;
5574 return iBit;
5575}
5576
5577
5578/**
5579 * Worker for RTFsFatVolOpen.
5580 *
5581 * @returns IPRT status code.
5582 * @param pThis The FAT VFS instance to initialize.
5583 * @param hVfsSelf The FAT VFS handle (no reference consumed).
5584 * @param hVfsBacking The file backing the alleged FAT file system.
5585 * Reference is consumed (via rtFsFatVol_Destroy).
5586 * @param fReadOnly Readonly or readwrite mount.
5587 * @param offBootSector The boot sector offset in bytes.
5588 * @param pErrInfo Where to return additional error info. Can be NULL.
5589 */
5590static int rtFsFatVolTryInit(PRTFSFATVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking,
5591 bool fReadOnly, uint64_t offBootSector, PRTERRINFO pErrInfo)
5592{
5593 /*
5594 * First initialize the state so that rtFsFatVol_Destroy won't trip up.
5595 */
5596 pThis->hVfsSelf = hVfsSelf;
5597 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsFatVol_Destroy releases it. */
5598 pThis->cbBacking = 0;
5599 pThis->offBootSector = offBootSector;
5600 pThis->offNanoUTC = RTTimeLocalDeltaNano();
5601 pThis->offMinUTC = pThis->offNanoUTC / RT_NS_1MIN;
5602 pThis->fReadOnly = fReadOnly;
5603 pThis->cReservedSectors = 1;
5604
5605 pThis->cbSector = 512;
5606 pThis->cbCluster = 512;
5607 pThis->cClusters = 0;
5608 pThis->offFirstCluster = 0;
5609 pThis->cbTotalSize = 0;
5610
5611 pThis->enmFatType = RTFSFATTYPE_INVALID;
5612 pThis->cFatEntries = 0;
5613 pThis->cFats = 0;
5614 pThis->cbFat = 0;
5615 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aoffFats); i++)
5616 pThis->aoffFats[i] = UINT64_MAX;
5617 pThis->pFatCache = NULL;
5618
5619 pThis->offRootDir = UINT64_MAX;
5620 pThis->idxRootDirCluster = UINT32_MAX;
5621 pThis->cRootDirEntries = UINT32_MAX;
5622 pThis->cbRootDir = 0;
5623 pThis->pRootDir = NULL;
5624
5625 pThis->uSerialNo = 0;
5626 pThis->szLabel[0] = '\0';
5627 pThis->szType[0] = '\0';
5628 pThis->cBootSectorCopies = 0;
5629 pThis->fFat32Flags = 0;
5630 pThis->offBootSectorCopies = UINT64_MAX;
5631 pThis->offFat32InfoSector = UINT64_MAX;
5632 RT_ZERO(pThis->Fat32InfoSector);
5633
5634 /*
5635 * Get stuff that may fail.
5636 */
5637 int rc = RTVfsFileQuerySize(hVfsBacking, &pThis->cbBacking);
5638 if (RT_FAILURE(rc))
5639 return rc;
5640 pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
5641
5642 /*
5643 * Read the boot sector and the following sector (start of the allocation
5644 * table unless it a FAT32 FS). We'll then validate the boot sector and
5645 * start of the FAT, expanding the BPB into the instance data.
5646 */
5647 union
5648 {
5649 uint8_t ab[512*2];
5650 uint16_t au16[512*2 / 2];
5651 uint32_t au32[512*2 / 4];
5652 FATBOOTSECTOR BootSector;
5653 FAT32INFOSECTOR InfoSector;
5654 } Buf;
5655 RT_ZERO(Buf);
5656
5657 rc = RTVfsFileReadAt(hVfsBacking, offBootSector, &Buf.BootSector, 512 * 2, NULL);
5658 if (RT_FAILURE(rc))
5659 return RTErrInfoSet(pErrInfo, rc, "Unable to read bootsect");
5660
5661 /*
5662 * Extract info from the BPB and validate the two special FAT entries.
5663 *
5664 * Check the DOS signature first. The PC-DOS 1.0 boot floppy does not have
5665 * a signature and we ASSUME this is the case for all floppies formated by it.
5666 */
5667 if (Buf.BootSector.uSignature != FATBOOTSECTOR_SIGNATURE)
5668 {
5669 if (Buf.BootSector.uSignature != 0)
5670 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "No DOS bootsector signature: %#06x", Buf.BootSector.uSignature);
5671 rc = rtFsFatVolTryInitDos1x(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
5672 }
5673 else
5674 rc = rtFsFatVolTryInitDos2Plus(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
5675 if (RT_FAILURE(rc))
5676 return rc;
5677
5678 /*
5679 * Calc shift counts.
5680 */
5681 pThis->cSectorByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbSector);
5682 pThis->cClusterByteShift = rtFsFatVolCalcByteShiftCount(pThis->cbCluster);
5683
5684 /*
5685 * Setup the FAT cache.
5686 */
5687 rc = rtFsFatClusterMap_Create(pThis, &Buf.ab[512], pErrInfo);
5688 if (RT_FAILURE(rc))
5689 return rc;
5690
5691 /*
5692 * Create the root directory fun.
5693 */
5694 if (pThis->idxRootDirCluster == UINT32_MAX)
5695 rc = rtFsFatDirShrd_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
5696 UINT32_MAX, pThis->offRootDir, pThis->cbRootDir, &pThis->pRootDir);
5697 else
5698 rc = rtFsFatDirShrd_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, UINT32_MAX /*offEntryInDir*/,
5699 pThis->idxRootDirCluster, UINT64_MAX, pThis->cbRootDir, &pThis->pRootDir);
5700 return rc;
5701}
5702
5703
5704/**
5705 * Opens a FAT file system volume.
5706 *
5707 * @returns IPRT status code.
5708 * @param hVfsFileIn The file or device backing the volume.
5709 * @param fReadOnly Whether to mount it read-only.
5710 * @param offBootSector The offset of the boot sector relative to the start
5711 * of @a hVfsFileIn. Pass 0 for floppies.
5712 * @param phVfs Where to return the virtual file system handle.
5713 * @param pErrInfo Where to return additional error information.
5714 */
5715RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, uint64_t offBootSector, PRTVFS phVfs, PRTERRINFO pErrInfo)
5716{
5717 /*
5718 * Quick input validation.
5719 */
5720 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
5721 *phVfs = NIL_RTVFS;
5722
5723 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
5724 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5725
5726 /*
5727 * Create a new FAT VFS instance and try initialize it using the given input file.
5728 */
5729 RTVFS hVfs = NIL_RTVFS;
5730 void *pvThis = NULL;
5731 int rc = RTVfsNew(&g_rtFsFatVolOps, sizeof(RTFSFATVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
5732 if (RT_SUCCESS(rc))
5733 {
5734 rc = rtFsFatVolTryInit((PRTFSFATVOL)pvThis, hVfs, hVfsFileIn, fReadOnly, offBootSector, pErrInfo);
5735 if (RT_SUCCESS(rc))
5736 *phVfs = hVfs;
5737 else
5738 RTVfsRelease(hVfs);
5739 }
5740 else
5741 RTVfsFileRelease(hVfsFileIn);
5742 return rc;
5743}
5744
5745
5746
5747
5748/**
5749 * Fills a range in the file with zeros in the most efficient manner.
5750 *
5751 * @returns IPRT status code.
5752 * @param hVfsFile The file to write to.
5753 * @param off Where to start filling with zeros.
5754 * @param cbZeros How many zero blocks to write.
5755 */
5756static int rtFsFatVolWriteZeros(RTVFSFILE hVfsFile, uint64_t off, uint32_t cbZeros)
5757{
5758 while (cbZeros > 0)
5759 {
5760 uint32_t cbToWrite = sizeof(g_abRTZero64K);
5761 if (cbToWrite > cbZeros)
5762 cbToWrite = cbZeros;
5763 int rc = RTVfsFileWriteAt(hVfsFile, off, g_abRTZero64K, cbToWrite, NULL);
5764 if (RT_FAILURE(rc))
5765 return rc;
5766 off += cbToWrite;
5767 cbZeros -= cbToWrite;
5768 }
5769 return VINF_SUCCESS;
5770}
5771
5772
5773/**
5774 * Formats a FAT volume.
5775 *
5776 * @returns IRPT status code.
5777 * @param hVfsFile The volume file.
5778 * @param offVol The offset into @a hVfsFile of the file.
5779 * Typically 0.
5780 * @param cbVol The size of the volume. Pass 0 if the rest of
5781 * hVfsFile should be used.
5782 * @param fFlags See RTFSFATVOL_FMT_F_XXX.
5783 * @param cbSector The logical sector size. Must be power of two.
5784 * Optional, pass zero to use 512.
5785 * @param cSectorsPerCluster Number of sectors per cluster. Power of two.
5786 * Optional, pass zero to auto detect.
5787 * @param enmFatType The FAT type (12, 16, 32) to use.
5788 * Optional, pass RTFSFATTYPE_INVALID for default.
5789 * @param cHeads The number of heads to report in the BPB.
5790 * Optional, pass zero to auto detect.
5791 * @param cSectorsPerTrack The number of sectors per track to put in the
5792 * BPB. Optional, pass zero to auto detect.
5793 * @param bMedia The media byte value and FAT ID to use.
5794 * Optional, pass zero to auto detect.
5795 * @param cRootDirEntries Number of root directory entries.
5796 * Optional, pass zero to auto detect.
5797 * @param cHiddenSectors Number of hidden sectors. Pass 0 for
5798 * unpartitioned media.
5799 * @param pErrInfo Additional error information, maybe. Optional.
5800 */
5801RTDECL(int) RTFsFatVolFormat(RTVFSFILE hVfsFile, uint64_t offVol, uint64_t cbVol, uint32_t fFlags, uint16_t cbSector,
5802 uint16_t cSectorsPerCluster, RTFSFATTYPE enmFatType, uint32_t cHeads, uint32_t cSectorsPerTrack,
5803 uint8_t bMedia, uint16_t cRootDirEntries, uint32_t cHiddenSectors, PRTERRINFO pErrInfo)
5804{
5805 int rc;
5806 uint32_t cFats = 2;
5807
5808 /*
5809 * Validate input.
5810 */
5811 if (!cbSector)
5812 cbSector = 512;
5813 else
5814 AssertMsgReturn( cbSector == 128
5815 || cbSector == 512
5816 || cbSector == 1024
5817 || cbSector == 4096,
5818 ("cbSector=%#x\n", cbSector),
5819 VERR_INVALID_PARAMETER);
5820 AssertMsgReturn(cSectorsPerCluster == 0 || (cSectorsPerCluster <= 128 && RT_IS_POWER_OF_TWO(cSectorsPerCluster)),
5821 ("cSectorsPerCluster=%#x\n", cSectorsPerCluster), VERR_INVALID_PARAMETER);
5822 if (bMedia != 0)
5823 {
5824 AssertMsgReturn(FAT_ID_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
5825 AssertMsgReturn(FATBPB_MEDIA_IS_VALID(bMedia), ("bMedia=%#x\n", bMedia), VERR_INVALID_PARAMETER);
5826 }
5827 AssertReturn(!(fFlags & ~RTFSFATVOL_FMT_F_VALID_MASK), VERR_INVALID_FLAGS);
5828 AssertReturn(enmFatType >= RTFSFATTYPE_INVALID && enmFatType < RTFSFATTYPE_END, VERR_INVALID_PARAMETER);
5829
5830 if (!cbVol)
5831 {
5832 uint64_t cbFile;
5833 rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
5834 AssertRCReturn(rc, rc);
5835 AssertMsgReturn(cbFile > offVol, ("cbFile=%#RX64 offVol=%#RX64\n", cbFile, offVol), VERR_INVALID_PARAMETER);
5836 cbVol = cbFile - offVol;
5837 }
5838 uint64_t const cSectorsInVol = cbVol / cbSector;
5839
5840 /*
5841 * Guess defaults if necessary.
5842 */
5843 if (!cSectorsPerCluster || !cHeads || !cSectorsPerTrack || !bMedia || !cRootDirEntries)
5844 {
5845 static struct
5846 {
5847 uint64_t cbVol;
5848 uint8_t bMedia;
5849 uint8_t cHeads;
5850 uint8_t cSectorsPerTrack;
5851 uint8_t cSectorsPerCluster;
5852 uint16_t cRootDirEntries;
5853 } s_aDefaults[] =
5854 {
5855 /* cbVol, bMedia, cHeads, cSectorsPTrk, cSectorsPClstr, cRootDirEntries */
5856 { 163840, 0xfe, /* cyl: 40,*/ 1, 8, 1, 64 },
5857 { 184320, 0xfc, /* cyl: 40,*/ 1, 9, 2, 64 },
5858 { 327680, 0xff, /* cyl: 40,*/ 2, 8, 2, 112 },
5859 { 368640, 0xfd, /* cyl: 40,*/ 2, 9, 2, 112 },
5860 { 737280, 0xf9, /* cyl: 80,*/ 2, 9, 2, 112 },
5861 { 1228800, 0xf9, /* cyl: 80,*/ 2, 15, 2, 112 },
5862 { 1474560, 0xf0, /* cyl: 80,*/ 2, 18, 1, 224 },
5863 { 2949120, 0xf0, /* cyl: 80,*/ 2, 36, 2, 224 },
5864 { 528482304, 0xf8, /* cyl: 1024,*/ 16, 63, 0, 512 }, // 504MB limit
5865 { UINT64_C(7927234560), 0xf8, /* cyl: 1024,*/ 240, 63, 0, 512 }, // 7.3GB limit
5866 { UINT64_C(8422686720), 0xf8, /* cyl: 1024,*/ 255, 63, 0, 512 }, // 7.84GB limit
5867
5868 };
5869 uint32_t iDefault = 0;
5870 while ( iDefault < RT_ELEMENTS(s_aDefaults) - 1U
5871 && cbVol > s_aDefaults[iDefault].cbVol)
5872 iDefault++;
5873 if (!cHeads)
5874 cHeads = s_aDefaults[iDefault].cHeads;
5875 if (!cSectorsPerTrack)
5876 cSectorsPerTrack = s_aDefaults[iDefault].cSectorsPerTrack;
5877 if (!bMedia)
5878 bMedia = s_aDefaults[iDefault].bMedia;
5879 if (!cRootDirEntries)
5880 cRootDirEntries = s_aDefaults[iDefault].cRootDirEntries;
5881 if (!cSectorsPerCluster)
5882 {
5883 cSectorsPerCluster = s_aDefaults[iDefault].cSectorsPerCluster;
5884 if (!cSectorsPerCluster)
5885 {
5886 uint32_t cbFat12Overhead = cbSector /* boot sector */
5887 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
5888 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
5889 uint32_t cbFat16Overhead = cbSector /* boot sector */
5890 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
5891 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
5892
5893 if ( enmFatType == RTFSFATTYPE_FAT12
5894 || cbVol <= cbFat12Overhead + FAT_MAX_FAT12_DATA_CLUSTERS * 4 * cbSector)
5895 {
5896 enmFatType = RTFSFATTYPE_FAT12;
5897 cSectorsPerCluster = 1;
5898 while ( cSectorsPerCluster < 128
5899 && cSectorsInVol
5900 > cbFat12Overhead / cbSector
5901 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT12_DATA_CLUSTERS
5902 + cSectorsPerCluster - 1)
5903 cSectorsPerCluster <<= 1;
5904 }
5905 else if ( enmFatType == RTFSFATTYPE_FAT16
5906 || cbVol <= cbFat16Overhead + FAT_MAX_FAT16_DATA_CLUSTERS * 128 * cbSector)
5907 {
5908 enmFatType = RTFSFATTYPE_FAT16;
5909 cSectorsPerCluster = 1;
5910 while ( cSectorsPerCluster < 128
5911 && cSectorsInVol
5912 > cbFat12Overhead / cbSector
5913 + (uint32_t)cSectorsPerCluster * FAT_MAX_FAT16_DATA_CLUSTERS
5914 + cSectorsPerCluster - 1)
5915 cSectorsPerCluster <<= 1;
5916 }
5917 else
5918 {
5919 /* The target here is keeping the FAT size below 8MB. Seems windows
5920 likes a minimum 4KB cluster size as wells as a max of 32KB (googling). */
5921 enmFatType = RTFSFATTYPE_FAT32;
5922 uint32_t cbFat32Overhead = cbSector * 32 /* boot sector, info sector, boot sector copies, reserved sectors */
5923 + _8M * cFats;
5924 if (cbSector >= _4K)
5925 cSectorsPerCluster = 1;
5926 else
5927 cSectorsPerCluster = _4K / cbSector;
5928 while ( cSectorsPerCluster < 128
5929 && cSectorsPerCluster * cbSector < _32K
5930 && cSectorsInVol > cbFat32Overhead / cbSector + (uint64_t)cSectorsPerCluster * _2M)
5931 cSectorsPerCluster <<= 1;
5932 }
5933 }
5934 }
5935 }
5936 Assert(cSectorsPerCluster);
5937 Assert(cRootDirEntries);
5938 uint32_t cbRootDir = RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector);
5939 uint32_t const cbCluster = cSectorsPerCluster * cbSector;
5940
5941 /*
5942 * If we haven't figured out the FAT type yet, do so.
5943 * The file system code determins the FAT based on cluster counts,
5944 * so we must do so here too.
5945 */
5946 if (enmFatType == RTFSFATTYPE_INVALID)
5947 {
5948 uint32_t cbFat12Overhead = cbSector /* boot sector */
5949 + RT_ALIGN_32(FAT_MAX_FAT12_TOTAL_CLUSTERS * 3 / 2, cbSector) * cFats /* FATs */
5950 + RT_ALIGN_32(cRootDirEntries * sizeof(FATDIRENTRY), cbSector) /* root dir */;
5951 if ( cbVol <= cbFat12Overhead + cbCluster
5952 || (cbVol - cbFat12Overhead) / cbCluster <= FAT_MAX_FAT12_DATA_CLUSTERS)
5953 enmFatType = RTFSFATTYPE_FAT12;
5954 else
5955 {
5956 uint32_t cbFat16Overhead = cbSector /* boot sector */
5957 + RT_ALIGN_32(FAT_MAX_FAT16_TOTAL_CLUSTERS * 2, cbSector) * cFats /* FATs */
5958 + cbRootDir;
5959 if ( cbVol <= cbFat16Overhead + cbCluster
5960 || (cbVol - cbFat16Overhead) / cbCluster <= FAT_MAX_FAT16_DATA_CLUSTERS)
5961 enmFatType = RTFSFATTYPE_FAT16;
5962 else
5963 enmFatType = RTFSFATTYPE_FAT32;
5964 }
5965 }
5966 if (enmFatType == RTFSFATTYPE_FAT32)
5967 cbRootDir = cbCluster;
5968
5969 /*
5970 * Calculate the FAT size and number of data cluster.
5971 *
5972 * Since the FAT size depends on how many data clusters there are, we start
5973 * with a minimum FAT size and maximum clust count, then recalucate it. The
5974 * result isn't necessarily stable, so we will only retry stabalizing the
5975 * result a few times.
5976 */
5977 uint32_t cbReservedFixed = enmFatType == RTFSFATTYPE_FAT32 ? 32 * cbSector : cbSector + cbRootDir;
5978 uint32_t cbFat = cbSector;
5979 if (cbReservedFixed + cbFat * cFats >= cbVol)
5980 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
5981 cbVol, cbReservedFixed, cbFat, cFats);
5982 uint32_t cMaxClusters = enmFatType == RTFSFATTYPE_FAT12 ? FAT_MAX_FAT12_DATA_CLUSTERS
5983 : enmFatType == RTFSFATTYPE_FAT16 ? FAT_MAX_FAT16_DATA_CLUSTERS
5984 : FAT_MAX_FAT12_DATA_CLUSTERS;
5985 uint32_t cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
5986 uint32_t cPrevClusters;
5987 uint32_t cTries = 4;
5988 do
5989 {
5990 cPrevClusters = cClusters;
5991 switch (enmFatType)
5992 {
5993 case RTFSFATTYPE_FAT12:
5994 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT12_TOTAL_CLUSTERS, cClusters) * 3 / 2;
5995 break;
5996 case RTFSFATTYPE_FAT16:
5997 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT16_TOTAL_CLUSTERS, cClusters) * 2;
5998 break;
5999 case RTFSFATTYPE_FAT32:
6000 cbFat = (uint32_t)RT_MIN(FAT_MAX_FAT32_TOTAL_CLUSTERS, cClusters) * 4;
6001 cbFat = RT_ALIGN_32(cbFat, _4K);
6002 break;
6003 default:
6004 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
6005 }
6006 cbFat = RT_ALIGN_32(cbFat, cbSector);
6007 if (cbReservedFixed + cbFat * cFats >= cbVol)
6008 return RTErrInfoSetF(pErrInfo, VERR_DISK_FULL, "volume is too small (cbVol=%#RX64 rsvd=%#x cbFat=%#x cFat=%#x)",
6009 cbVol, cbReservedFixed, cbFat, cFats);
6010 cClusters = (uint32_t)RT_MIN((cbVol - cbReservedFixed - cbFat * cFats) / cbCluster, cMaxClusters);
6011 } while ( cClusters != cPrevClusters
6012 && cTries-- > 0);
6013 uint64_t const cTotalSectors = cClusters * (uint64_t)cSectorsPerCluster + (cbReservedFixed + cbFat * cFats) / cbSector;
6014
6015 /*
6016 * Check that the file system type and cluster count matches up. If they
6017 * don't the type will be misdetected.
6018 *
6019 * Note! These assertions could trigger if the above calculations are wrong.
6020 */
6021 switch (enmFatType)
6022 {
6023 case RTFSFATTYPE_FAT12:
6024 AssertMsgReturn(cClusters >= FAT_MIN_FAT12_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT12_DATA_CLUSTERS,
6025 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
6026 break;
6027 case RTFSFATTYPE_FAT16:
6028 AssertMsgReturn(cClusters >= FAT_MIN_FAT16_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT16_DATA_CLUSTERS,
6029 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
6030 break;
6031 case RTFSFATTYPE_FAT32:
6032 AssertMsgReturn(cClusters >= FAT_MIN_FAT32_DATA_CLUSTERS && cClusters <= FAT_MAX_FAT32_DATA_CLUSTERS,
6033 ("cClusters=%#x\n", cClusters), VERR_OUT_OF_RANGE);
6034 RT_FALL_THRU();
6035 default:
6036 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
6037 }
6038
6039 /*
6040 * Okay, create the boot sector.
6041 */
6042 size_t cbBuf = RT_MAX(RT_MAX(_64K, cbCluster), cbSector * 2U);
6043 uint8_t *pbBuf = (uint8_t *)RTMemTmpAllocZ(cbBuf);
6044 AssertReturn(pbBuf, VERR_NO_TMP_MEMORY);
6045
6046 const char *pszLastOp = "boot sector";
6047 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pbBuf;
6048 pBootSector->abJmp[0] = 0xeb;
6049 pBootSector->abJmp[1] = RT_UOFFSETOF(FATBOOTSECTOR, Bpb)
6050 + (enmFatType == RTFSFATTYPE_FAT32 ? sizeof(FAT32EBPB) : sizeof(FATEBPB)) - 2;
6051 pBootSector->abJmp[2] = 0x90;
6052 memcpy(pBootSector->achOemName, enmFatType == RTFSFATTYPE_FAT32 ? "FAT32 " : "IPRT 6.2", sizeof(pBootSector->achOemName));
6053 pBootSector->Bpb.Bpb331.cbSector = (uint16_t)cbSector;
6054 pBootSector->Bpb.Bpb331.cSectorsPerCluster = (uint8_t)cSectorsPerCluster;
6055 pBootSector->Bpb.Bpb331.cReservedSectors = enmFatType == RTFSFATTYPE_FAT32 ? cbReservedFixed / cbSector : 1;
6056 pBootSector->Bpb.Bpb331.cFats = (uint8_t)cFats;
6057 pBootSector->Bpb.Bpb331.cMaxRootDirEntries = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cRootDirEntries;
6058 pBootSector->Bpb.Bpb331.cTotalSectors16 = cTotalSectors <= UINT16_MAX ? (uint16_t)cTotalSectors : 0;
6059 pBootSector->Bpb.Bpb331.bMedia = bMedia;
6060 pBootSector->Bpb.Bpb331.cSectorsPerFat = enmFatType == RTFSFATTYPE_FAT32 ? 0 : cbFat / cbSector;
6061 pBootSector->Bpb.Bpb331.cSectorsPerTrack = cSectorsPerTrack;
6062 pBootSector->Bpb.Bpb331.cTracksPerCylinder = cHeads;
6063 pBootSector->Bpb.Bpb331.cHiddenSectors = cHiddenSectors;
6064 /* XP barfs if both cTotalSectors32 and cTotalSectors16 are set */
6065 pBootSector->Bpb.Bpb331.cTotalSectors32 = cTotalSectors <= UINT32_MAX && pBootSector->Bpb.Bpb331.cTotalSectors16 == 0
6066 ? (uint32_t)cTotalSectors : 0;
6067 if (enmFatType != RTFSFATTYPE_FAT32)
6068 {
6069 pBootSector->Bpb.Ebpb.bInt13Drive = 0;
6070 pBootSector->Bpb.Ebpb.bReserved = 0;
6071 pBootSector->Bpb.Ebpb.bExtSignature = FATEBPB_SIGNATURE;
6072 pBootSector->Bpb.Ebpb.uSerialNumber = RTRandU32();
6073 memset(pBootSector->Bpb.Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Ebpb.achLabel));
6074 memcpy(pBootSector->Bpb.Ebpb.achType, enmFatType == RTFSFATTYPE_FAT12 ? "FAT12 " : "FAT16 ",
6075 sizeof(pBootSector->Bpb.Ebpb.achType));
6076 }
6077 else
6078 {
6079 pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 = cbFat / cbSector;
6080 pBootSector->Bpb.Fat32Ebpb.fFlags = 0;
6081 pBootSector->Bpb.Fat32Ebpb.uVersion = FAT32EBPB_VERSION_0_0;
6082 pBootSector->Bpb.Fat32Ebpb.uRootDirCluster = FAT_FIRST_DATA_CLUSTER;
6083 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo = 1;
6084 pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo = 6;
6085 RT_ZERO(pBootSector->Bpb.Fat32Ebpb.abReserved);
6086
6087 pBootSector->Bpb.Fat32Ebpb.bInt13Drive = 0;
6088 pBootSector->Bpb.Fat32Ebpb.bReserved = 0;
6089 pBootSector->Bpb.Fat32Ebpb.bExtSignature = FATEBPB_SIGNATURE;
6090 pBootSector->Bpb.Fat32Ebpb.uSerialNumber = RTRandU32();
6091 memset(pBootSector->Bpb.Fat32Ebpb.achLabel, ' ', sizeof(pBootSector->Bpb.Fat32Ebpb.achLabel));
6092 if (cTotalSectors > UINT32_MAX)
6093 pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 = cTotalSectors;
6094 else
6095 memcpy(pBootSector->Bpb.Fat32Ebpb.u.achType, "FAT32 ", sizeof(pBootSector->Bpb.Fat32Ebpb.u.achType));
6096 }
6097 pbBuf[pBootSector->abJmp[1] + 2 + 0] = 0xcd; /* int 18h */ /** @todo find/implement booting of next boot device. */
6098 pbBuf[pBootSector->abJmp[1] + 2 + 1] = 0x18;
6099 pbBuf[pBootSector->abJmp[1] + 2 + 2] = 0xcc; /* int3 */
6100 pbBuf[pBootSector->abJmp[1] + 2 + 3] = 0xcc;
6101
6102 pBootSector->uSignature = FATBOOTSECTOR_SIGNATURE;
6103 if (cbSector != sizeof(*pBootSector))
6104 *(uint16_t *)&pbBuf[cbSector - 2] = FATBOOTSECTOR_SIGNATURE; /** @todo figure out how disks with non-512 byte sectors work! */
6105
6106 rc = RTVfsFileWriteAt(hVfsFile, offVol, pBootSector, cbSector, NULL);
6107 uint32_t const offFirstFat = pBootSector->Bpb.Bpb331.cReservedSectors * cbSector;
6108
6109 /*
6110 * Write the FAT32 info sector, 3 boot sector copies, and zero fill
6111 * the other reserved sectors.
6112 */
6113 if (RT_SUCCESS(rc) && enmFatType == RTFSFATTYPE_FAT32)
6114 {
6115 pszLastOp = "fat32 info sector";
6116 PFAT32INFOSECTOR pInfoSector = (PFAT32INFOSECTOR)&pbBuf[cbSector]; /* preserve the boot sector. */
6117 RT_ZERO(*pInfoSector);
6118 pInfoSector->uSignature1 = FAT32INFOSECTOR_SIGNATURE_1;
6119 pInfoSector->uSignature2 = FAT32INFOSECTOR_SIGNATURE_2;
6120 pInfoSector->uSignature3 = FAT32INFOSECTOR_SIGNATURE_3;
6121 pInfoSector->cFreeClusters = cClusters - 1; /* ASSUMES 1 cluster for the root dir. */
6122 pInfoSector->cLastAllocatedCluster = FAT_FIRST_DATA_CLUSTER;
6123 rc = RTVfsFileWriteAt(hVfsFile, offVol + cbSector, pInfoSector, cbSector, NULL);
6124
6125 uint32_t iSector = 2;
6126 if (RT_SUCCESS(rc))
6127 {
6128 pszLastOp = "fat32 unused reserved sectors";
6129 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
6130 (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo - iSector) * cbSector);
6131 iSector = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo;
6132 }
6133
6134 if (RT_SUCCESS(rc))
6135 {
6136 pszLastOp = "boot sector copy";
6137 for (uint32_t i = 0; i < 3 && RT_SUCCESS(rc); i++, iSector++)
6138 rc = RTVfsFileWriteAt(hVfsFile, offVol + iSector * cbSector, pBootSector, cbSector, NULL);
6139 }
6140
6141 if (RT_SUCCESS(rc))
6142 {
6143 pszLastOp = "fat32 unused reserved sectors";
6144 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + iSector * cbSector,
6145 (pBootSector->Bpb.Bpb331.cReservedSectors - iSector) * cbSector);
6146 }
6147 }
6148
6149 /*
6150 * The FATs.
6151 */
6152 if (RT_SUCCESS(rc))
6153 {
6154 pszLastOp = "fat";
6155 pBootSector = NULL; /* invalid */
6156 RT_BZERO(pbBuf, cbSector);
6157 switch (enmFatType)
6158 {
6159 case RTFSFATTYPE_FAT32:
6160 pbBuf[11] = 0x0f; /* EOC for root dir*/
6161 pbBuf[10] = 0xff;
6162 pbBuf[9] = 0xff;
6163 pbBuf[8] = 0xff;
6164 pbBuf[7] = 0x0f; /* Formatter's EOC, followed by signed extend FAT ID. */
6165 pbBuf[6] = 0xff;
6166 pbBuf[5] = 0xff;
6167 pbBuf[4] = 0xff;
6168 RT_FALL_THRU();
6169 case RTFSFATTYPE_FAT16:
6170 pbBuf[3] = 0xff;
6171 RT_FALL_THRU();
6172 case RTFSFATTYPE_FAT12:
6173 pbBuf[2] = 0xff;
6174 pbBuf[1] = 0xff;
6175 pbBuf[0] = bMedia; /* FAT ID */
6176 break;
6177 default: AssertFailed();
6178 }
6179 for (uint32_t iFatCopy = 0; iFatCopy < cFats && RT_SUCCESS(rc); iFatCopy++)
6180 {
6181 rc = RTVfsFileWriteAt(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy, pbBuf, cbSector, NULL);
6182 if (RT_SUCCESS(rc) && cbFat > cbSector)
6183 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * iFatCopy + cbSector, cbFat - cbSector);
6184 }
6185 }
6186
6187 /*
6188 * The root directory.
6189 */
6190 if (RT_SUCCESS(rc))
6191 {
6192 /** @todo any mandatory directory entries we need to fill in here? */
6193 pszLastOp = "root dir";
6194 rc = rtFsFatVolWriteZeros(hVfsFile, offVol + offFirstFat + cbFat * cFats, cbRootDir);
6195 }
6196
6197 /*
6198 * If long format, fill the rest of the disk with 0xf6.
6199 */
6200 AssertCompile(RTFSFATVOL_FMT_F_QUICK != 0);
6201 if (RT_SUCCESS(rc) && !(fFlags & RTFSFATVOL_FMT_F_QUICK))
6202 {
6203 pszLastOp = "formatting data clusters";
6204 uint64_t offCur = offFirstFat + cbFat * cFats + cbRootDir;
6205 uint64_t cbLeft = cTotalSectors * cbSector;
6206 if (cbVol - cbLeft <= _256K) /* HACK ALERT! Format to end of volume if it's a cluster rounding thing. */
6207 cbLeft = cbVol;
6208 if (cbLeft > offCur)
6209 {
6210 cbLeft -= offCur;
6211 offCur += offVol;
6212
6213 memset(pbBuf, 0xf6, cbBuf);
6214 while (cbLeft > 0)
6215 {
6216 size_t cbToWrite = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
6217 rc = RTVfsFileWriteAt(hVfsFile, offCur, pbBuf, cbToWrite, NULL);
6218 if (RT_SUCCESS(rc))
6219 {
6220 offCur += cbToWrite;
6221 cbLeft -= cbToWrite;
6222 }
6223 else
6224 break;
6225 }
6226 }
6227 }
6228
6229 /*
6230 * Done.
6231 */
6232 RTMemTmpFree(pbBuf);
6233 if (RT_SUCCESS(rc))
6234 return rc;
6235 return RTErrInfoSet(pErrInfo, rc, pszLastOp);
6236}
6237
6238
6239/**
6240 * Formats a 1.44MB floppy image.
6241 *
6242 * @returns IPRT status code.
6243 * @param hVfsFile The image.
6244 */
6245RTDECL(int) RTFsFatVolFormat144(RTVFSFILE hVfsFile, bool fQuick)
6246{
6247 return RTFsFatVolFormat(hVfsFile, 0 /*offVol*/, 1474560, fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
6248 512 /*cbSector*/, 1 /*cSectorsPerCluster*/, RTFSFATTYPE_FAT12, 2 /*cHeads*/, 18 /*cSectors*/,
6249 0xf0 /*bMedia*/, 224 /*cRootDirEntries*/, 0 /*cHiddenSectors*/, NULL /*pErrInfo*/);
6250}
6251
6252
6253/**
6254 * Formats a 2.88MB floppy image.
6255 *
6256 * @returns IPRT status code.
6257 * @param hVfsFile The image.
6258 */
6259RTDECL(int) RTFsFatVolFormat288(RTVFSFILE hVfsFile, bool fQuick)
6260{
6261 return RTFsFatVolFormat(hVfsFile, 0 /*offVol*/, 2949120, fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
6262 512 /*cbSector*/, 2 /*cSectorsPerCluster*/, RTFSFATTYPE_FAT12, 2 /*cHeads*/, 36 /*cSectors*/,
6263 0xf0 /*bMedia*/, 224 /*cRootDirEntries*/, 0 /*cHiddenSectors*/, NULL /*pErrInfo*/);
6264}
6265
6266
6267/**
6268 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
6269 */
6270static DECLCALLBACK(int) rtVfsChainFatVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
6271 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
6272{
6273 RT_NOREF(pProviderReg);
6274
6275 /*
6276 * Basic checks.
6277 */
6278 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
6279 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
6280 if ( pElement->enmType != RTVFSOBJTYPE_VFS
6281 && pElement->enmType != RTVFSOBJTYPE_DIR)
6282 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
6283 if (pElement->cArgs > 1)
6284 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
6285
6286 /*
6287 * Parse the flag if present, save in pElement->uProvider.
6288 */
6289 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
6290 if (pElement->cArgs > 0)
6291 {
6292 const char *psz = pElement->paArgs[0].psz;
6293 if (*psz)
6294 {
6295 if (!strcmp(psz, "ro"))
6296 fReadOnly = true;
6297 else if (!strcmp(psz, "rw"))
6298 fReadOnly = false;
6299 else
6300 {
6301 *poffError = pElement->paArgs[0].offSpec;
6302 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
6303 }
6304 }
6305 }
6306
6307 pElement->uProvider = fReadOnly;
6308 return VINF_SUCCESS;
6309}
6310
6311
6312/**
6313 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
6314 */
6315static DECLCALLBACK(int) rtVfsChainFatVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
6316 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
6317 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
6318{
6319 RT_NOREF(pProviderReg, pSpec, poffError);
6320
6321 int rc;
6322 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
6323 if (hVfsFileIn != NIL_RTVFSFILE)
6324 {
6325 RTVFS hVfs;
6326 rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, 0, &hVfs, pErrInfo);
6327 RTVfsFileRelease(hVfsFileIn);
6328 if (RT_SUCCESS(rc))
6329 {
6330 *phVfsObj = RTVfsObjFromVfs(hVfs);
6331 RTVfsRelease(hVfs);
6332 if (*phVfsObj != NIL_RTVFSOBJ)
6333 return VINF_SUCCESS;
6334 rc = VERR_VFS_CHAIN_CAST_FAILED;
6335 }
6336 }
6337 else
6338 rc = VERR_VFS_CHAIN_CAST_FAILED;
6339 return rc;
6340}
6341
6342
6343/**
6344 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
6345 */
6346static DECLCALLBACK(bool) rtVfsChainFatVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
6347 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
6348 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
6349{
6350 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
6351 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
6352 || !pReuseElement->paArgs[0].uProvider)
6353 return true;
6354 return false;
6355}
6356
6357
6358/** VFS chain element 'file'. */
6359static RTVFSCHAINELEMENTREG g_rtVfsChainFatVolReg =
6360{
6361 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
6362 /* fReserved = */ 0,
6363 /* pszName = */ "fat",
6364 /* ListEntry = */ { NULL, NULL },
6365 /* pszHelp = */ "Open a FAT file system, requires a file object on the left side.\n"
6366 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
6367 /* pfnValidate = */ rtVfsChainFatVol_Validate,
6368 /* pfnInstantiate = */ rtVfsChainFatVol_Instantiate,
6369 /* pfnCanReuseElement = */ rtVfsChainFatVol_CanReuseElement,
6370 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
6371};
6372
6373RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainFatVolReg, rtVfsChainFatVolReg);
6374
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