VirtualBox

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

最後變更 在這個檔案從102899是 102899,由 vboxsync 提交於 14 月 前

Runtime/common/fatvfs.cpp: Need to reset the S/G buffer when flushing the cluster map before writing the data, regression from r158845

The cluster map flushing code is directly modifying stuff in the S/G buffer structure instead of using the API but does it incorrectly.
This results in the members for the current segment pointer and remaining bytes remaining in the current segment to be NULL or 0 respectively.
The lower layer called through RTVfsFileSgWrite() will get a NULL pointer and 0 size for the current buffer when trying to access the S/G buffer
(stdfile in this case) causing no data to be written.
This worked before r158845 because the lower code would not actually use the S/G buffer but traverse the segment array directly not caring about
RTSGBUF::pvSegCur and RTSGBUF::cbSegLeft but was changed to use the RTSgBuf* API which will return NULL if both RTSGBUF::pvSegCur and RTSGBUF::cbSegLeft
are NULL or 0 respectively.

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

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