dosfstools  4.2
About: dosfstools are utilities to create, check and label (MS-DOS) FAT filesystems.
  Fossies Dox: dosfstools-4.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

check.c
Go to the documentation of this file.
1 /* check.c - Check and repair a PC/MS-DOS filesystem
2 
3  Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4  Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5  Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
6  Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
7  Copyright (C) 2017-2021 Pali Roh├ír <pali.rohar@gmail.com>
8 
9  This program is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program. If not, see <http://www.gnu.org/licenses/>.
21 
22  The complete text of the GNU General Public License
23  can be found in /usr/share/common-licenses/GPL-3 file.
24 */
25 
26 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
27  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <wctype.h>
36 
37 #include "common.h"
38 #include "fsck.fat.h"
39 #include "io.h"
40 #include "fat.h"
41 #include "file.h"
42 #include "lfn.h"
43 #include "check.h"
44 #include "boot.h"
45 #include "charconv.h"
46 
47 
48 /* the longest path on the filesystem that can be handled by path_name() */
49 #define PATH_NAME_MAX 1023
50 
51 static DOS_FILE *root;
52 
53 /* get start field of a dir entry */
54 #define FSTART(p,fs) \
55  ((uint32_t)le16toh(p->dir_ent.start) | \
56  (fs->fat_bits == 32 ? (uint32_t)le16toh(p->dir_ent.starthi) << 16 : 0))
57 
58 #define MODIFY(p,i,v) \
59  do { \
60  if (p->offset) { \
61  p->dir_ent.i = v; \
62  fs_write(p->offset+offsetof(DIR_ENT,i), \
63  sizeof(p->dir_ent.i),&p->dir_ent.i); \
64  } \
65  } while(0)
66 
67 #define MODIFY_START(p,v,fs) \
68  do { \
69  uint32_t __v = (v); \
70  if (!p->offset) { \
71  /* writing to fake entry for FAT32 root dir */ \
72  if (!__v) die("Oops, deleting FAT32 root dir!"); \
73  fs->root_cluster = __v; \
74  p->dir_ent.start = htole16(__v&0xffff); \
75  p->dir_ent.starthi = htole16(__v>>16); \
76  __v = htole32(__v); \
77  fs_write(offsetof(struct boot_sector,root_cluster), \
78  sizeof(((struct boot_sector *)0)->root_cluster), \
79  &__v); \
80  } \
81  else { \
82  MODIFY(p,start,htole16((__v)&0xffff)); \
83  if (fs->fat_bits == 32) \
84  MODIFY(p,starthi,htole16((__v)>>16)); \
85  } \
86  } while(0)
87 
88 /**
89  * Construct a full path (starting with '/') for the specified dentry,
90  * relative to the partition. All components are "long" names where possible.
91  *
92  * @param[in] file Information about dentry (file or directory) of interest
93  *
94  * return Pointer to static string containing file's full path
95  */
96 static char *path_name(DOS_FILE * file)
97 {
98  static char path[PATH_NAME_MAX * 2];
99 
100  if (!file)
101  *path = 0; /* Reached the root directory */
102  else {
103  if (strlen(path_name(file->parent)) > PATH_NAME_MAX)
104  die("Path name too long.");
105  if (strcmp(path, "/") != 0)
106  strcat(path, "/");
107 
108  /* Append the long name to the path,
109  * or the short name if there isn't a long one
110  */
111  strcpy(strrchr(path, 0),
112  file->lfn ? file->lfn : file_name(file->dir_ent.name));
113  }
114  return path;
115 }
116 
117 static const char *month_str[] =
118  { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
119 
120 static char *file_stat(DOS_FILE * file)
121 {
122  static char temp[100];
123  unsigned int hours, minutes, secs, day, month, year;
124  unsigned short time, date;
125 
126  time = le16toh(file->dir_ent.time);
127  date = le16toh(file->dir_ent.date);
128  year = 1980 + (date >> 9);
129  month = ((date >> 5) & 15);
130  if (month < 1) month = 1;
131  else if (month > 12) month = 12;
132  day = (date & 31);
133  if (day < 1) day = 1;
134  hours = (time >> 11);
135  if (hours > 23) hours = 23;
136  minutes = ((time >> 5) & 63);
137  if (minutes > 59) minutes = 59;
138  secs = (time & 31) * 2;
139  if (secs > 59) secs = 59;
140  sprintf(temp, " Size %u bytes, date %02u:%02u:%02u %s %02u %4u",
141  le32toh(file->dir_ent.size), hours, minutes, secs, month_str[month-1], day, year);
142  return temp;
143 }
144 
145 static int bad_name(DOS_FILE * file)
146 {
147  int i, spc, suspicious = 0;
148  const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:.";
149  const unsigned char *name = file->dir_ent.name;
150  const unsigned char *ext = name + 8;
151 
152  /* do not check synthetic FAT32 root entry */
153  if (!file->offset)
154  return 0;
155 
156  /* check if we have neither a long filename nor a short name */
157  if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) {
158  return 1;
159  }
160 
161  /* don't complain about the dummy 11 bytes used by patched Linux
162  kernels */
163  if (file->dir_ent.lcase & FAT_NO_83NAME)
164  return 0;
165 
166  for (i = 0; i < MSDOS_NAME; i++) {
167  if ((name[i] < ' ' && !(i == 0 && name[0] == 0x05)) || name[i] == 0x7f)
168  return 1;
169  if (name[i] > 0x7f)
170  ++suspicious;
171  if (strchr(bad_chars, name[i]))
172  return 1;
173  }
174 
175  if (name[0] == ' ')
176  return 1;
177 
178  if (no_spaces_in_sfns) {
179  spc = 0;
180  for (i = 0; i < 8; i++) {
181  if (name[i] == ' ')
182  spc = 1;
183  else if (spc)
184  /* non-space after a space not allowed, space terminates the name
185  * part */
186  return 1;
187  }
188 
189  spc = 0;
190  for (i = 0; i < 3; i++) {
191  if (ext[i] == ' ')
192  spc = 1;
193  else if (spc)
194  /* non-space after a space not allowed, space terminates the ext
195  * part */
196  return 1;
197  }
198  }
199 
200  /* Under GEMDOS, chars >= 128 are never allowed. */
201  if (atari_format && suspicious)
202  return 1;
203 
204  /* Under MS-DOS and Windows, chars >= 128 in short names are valid
205  * (but these characters can be visualised differently depending on
206  * local codepage: CP437, CP866, etc). The chars are all basically ok,
207  * so we shouldn't auto-correct such names. */
208  return 0;
209 }
210 
211 static void lfn_remove(off_t from, off_t to)
212 {
213  DIR_ENT empty;
214 
215  /* New dir entry is zeroed except first byte, which is set to 0xe5.
216  * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
217  * a directory at the first zero entry...
218  */
219  memset(&empty, 0, sizeof(empty));
220  empty.name[0] = DELETED_FLAG;
221 
222  for (; from < to; from += sizeof(empty)) {
223  fs_write(from, sizeof(DIR_ENT), &empty);
224  }
225 }
226 
227 static void drop_file(DOS_FS * fs, DOS_FILE * file)
228 {
229  (void) fs;
230 
231  MODIFY(file, name[0], DELETED_FLAG);
232  if (file->lfn)
233  lfn_remove(file->lfn_offset, file->offset);
234  --n_files;
235 }
236 
237 static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters)
238 {
239  int deleting;
240  uint32_t walk, next;
241 
242  walk = FSTART(file, fs);
243  if ((deleting = !clusters))
244  MODIFY_START(file, 0, fs);
245  while (walk > 0 && walk != -1) {
246  next = next_cluster(fs, walk);
247  if (deleting)
248  set_fat(fs, walk, 0);
249  else if ((deleting = !--clusters))
250  set_fat(fs, walk, -1);
251  walk = next;
252  }
253 }
254 
255 static void auto_rename(DOS_FILE * file)
256 {
257  DOS_FILE *first, *walk;
258  uint32_t number;
259 
260  if (!file->offset) {
261  printf("Cannot rename FAT32 root dir\n");
262  return; /* cannot rename FAT32 root dir */
263  }
264  first = file->parent ? file->parent->first : root;
265  number = 0;
266  while (1) {
267  char num[8];
268  sprintf(num, "%07lu", (unsigned long)number);
269  memcpy(file->dir_ent.name, "FSCK", 4);
270  memcpy(file->dir_ent.name + 4, num, 7);
271  for (walk = first; walk; walk = walk->next)
272  if (walk != file
273  && !strncmp((const char *)walk->dir_ent.name,
274  (const char *)file->dir_ent.name, MSDOS_NAME))
275  break;
276  if (!walk) {
277  if (file->dir_ent.lcase & FAT_NO_83NAME) {
278  /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
279  present */
280  file->dir_ent.lcase &= ~FAT_NO_83NAME;
281  /* reset the attributes, only keep DIR and VOLUME */
282  file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
283  fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
284  } else {
285  fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
286  }
287  if (file->lfn) {
288  lfn_remove(file->lfn_offset, file->offset);
289  file->lfn = NULL;
290  }
291  return;
292  }
293  number++;
294  if (number > 9999999) {
295  die("Too many files need repair.");
296  }
297  }
298  die("Can't generate a unique name.");
299 }
300 
301 static void rename_file(DOS_FILE * file)
302 {
303  unsigned char name[46];
304  unsigned char *walk, *here;
305 
306  if (!file->offset) {
307  printf("Cannot rename FAT32 root dir\n");
308  return; /* cannot rename FAT32 root dir */
309  }
310  while (1) {
311  if (get_line("New name", (char *)name, 45)) {
312  if ((here = (unsigned char *)strchr((const char *)name, '\n')))
313  *here = 0;
314  for (walk = (unsigned char *)strrchr((const char *)name, 0);
315  walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
316  walk[1] = 0;
317  for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
318  if (file_cvt(walk, file->dir_ent.name)) {
319  if (file->dir_ent.lcase & FAT_NO_83NAME) {
320  /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
321  present */
322  file->dir_ent.lcase &= ~FAT_NO_83NAME;
323  /* reset the attributes, only keep DIR and VOLUME */
324  file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
325  fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
326  } else {
327  fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
328  }
329  if (file->lfn) {
330  lfn_remove(file->lfn_offset, file->offset);
331  file->lfn = NULL;
332  }
333  return;
334  }
335  }
336  }
337 }
338 
339 static uint32_t scan_free_entry(DOS_FS * fs, DOS_FILE * this)
340 {
341  uint32_t clu_num, offset;
342  int i;
343  DIR_ENT de;
344 
345  i = 2 * sizeof(DIR_ENT); /* Skip '.' and '..' slots */
346  clu_num = FSTART(this, fs);
347  while (clu_num > 0 && clu_num != -1) {
348  offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
349  fs_read(offset, sizeof(DIR_ENT), &de);
350  if (IS_FREE(de.name))
351  return offset;
352  i += sizeof(DIR_ENT);
353  if (!(i % fs->cluster_size))
354  if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
355  break;
356  }
357 
358  return 0;
359 }
360 
361 static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dotdot)
362 {
363  const char *name, *ent;
364  uint32_t new_offset, start;
365 
366  if (dotdot) {
367  name = "..";
368  ent = MSDOS_DOTDOT;
369  if (!file->parent->parent) {
370  start = 0;
371  } else {
372  start = FSTART(file->parent->parent, fs);
373  if (start == fs->root_cluster)
374  start = 0;
375  }
376  } else {
377  name = ".";
378  ent = MSDOS_DOT;
379  start = FSTART(file->parent, fs);
380  }
381 
382  if (!(file->dir_ent.attr & ATTR_DIR) || (FSTART(file, fs) != start) ||
383  strncmp((const char *)(file->dir_ent.name), ent, MSDOS_NAME)) {
384 
385  if (IS_FREE(file->dir_ent.name)) {
386  printf("%s\n Expected a valid '%s' entry in the %s slot, found free entry.\n",
387  path_name(file->parent), name, dotdot ? "second" : "first");
388  switch (get_choice(1, " Creating.",
389  2,
390  1, "Create entry",
391  2, "Drop parent")) {
392  case 1:
393  goto conjure;
394  case 2:
395  drop_file(fs, file->parent);
396  return 1;
397  }
398  }
399 
400  if (!strncmp((const char *)(file->dir_ent.name), ent, MSDOS_NAME)) {
401  printf("%s\n Invalid '%s' entry in the %s slot. Fixing.\n",
402  path_name(file->parent), name, dotdot ? "second" : "first");
403  MODIFY_START(file, start, fs);
404  MODIFY(file, attr, ATTR_DIR);
405  } else {
406  printf("%s\n Expected a valid '%s' entry in this slot.\n",
407  path_name(file), name);
408  switch (get_choice(3, " Moving entry down.",
409  3,
410  1, "Drop entry",
411  2, "Drop parent",
412  3, "Move entry down")) {
413  case 1:
414  drop_file(fs, file);
415  goto conjure;
416  case 2:
417  drop_file(fs, file->parent);
418  return 1;
419  case 3:
420  new_offset = scan_free_entry(fs, file->parent);
421  if (!new_offset) {
422  printf("No free entry found.\n");
423  return 0;
424  }
425 
426  fs_write(new_offset, sizeof(file->dir_ent), &file->dir_ent);
427  goto conjure;
428  }
429  }
430  }
431  if (file->dir_ent.lcase & FAT_NO_83NAME) {
432  /* Some versions of mtools write these directory entries with random data in
433  this field. */
434  printf("%s\n Is a dot with no 8.3 name flag set, clearing.\n", path_name(file));
435  file->dir_ent.lcase &= ~FAT_NO_83NAME;
436  MODIFY(file, lcase, file->dir_ent.lcase);
437  }
438 
439  return 0;
440 
441 conjure:
442  memset(&file->dir_ent, 0, sizeof(DIR_ENT));
443  memcpy(file->dir_ent.name, ent, MSDOS_NAME);
444  fs_write(file->offset, sizeof(file->dir_ent), &file->dir_ent);
445  MODIFY_START(file, start, fs);
446  MODIFY(file, attr, ATTR_DIR);
447  return 0;
448 }
449 
450 static int check_file(DOS_FS * fs, DOS_FILE * file)
451 {
452  DOS_FILE *owner;
453  int restart;
454  uint32_t parent, grandp, curr, this, clusters, prev, walk, clusters2;
455 
456  if (IS_FREE(file->dir_ent.name))
457  return 0;
458 
459  if (file->dir_ent.attr & ATTR_DIR) {
460  if (le32toh(file->dir_ent.size)) {
461  printf("%s\n Directory has non-zero size. Fixing it.\n",
462  path_name(file));
463  MODIFY(file, size, htole32(0));
464  }
465  if (FSTART(file, fs) == 0) {
466  printf("%s\n Start does point to root directory. Deleting dir. \n",
467  path_name(file));
468  MODIFY(file, name[0], DELETED_FLAG);
469  return 0;
470  }
471  if (file->parent) {
472  parent = FSTART(file->parent, fs);
473  grandp = file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
474  if (fs->root_cluster && grandp == fs->root_cluster)
475  grandp = 0;
476 
477  if (FSTART(file, fs) == parent) {
478  printf("%s\n Start does point to containing directory. Deleting entry.\n",
479  path_name(file));
480  MODIFY(file, name[0], DELETED_FLAG);
481  MODIFY_START(file, 0, fs);
482  return 0;
483  }
484  if (FSTART(file, fs) == grandp) {
485  printf("%s\n Start does point to containing directory's parent. Deleting entry.\n",
486  path_name(file));
487  MODIFY(file, name[0], DELETED_FLAG);
488  MODIFY_START(file, 0, fs);
489  return 0;
490  }
491  }
492  }
493  if (FSTART(file, fs) == 1) {
494  printf("%s\n Bad start cluster 1. Truncating file.\n",
495  path_name(file));
496  if (!file->offset)
497  die("Bad FAT32 root directory! (bad start cluster 1)\n");
498  MODIFY_START(file, 0, fs);
499  }
500  if (FSTART(file, fs) >= fs->data_clusters + 2) {
501  printf
502  ("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n",
503  path_name(file), (unsigned long)FSTART(file, fs),
504  (unsigned long)(fs->data_clusters + 1));
505  if (!file->offset)
506  die("Bad FAT32 root directory! (start cluster beyond limit: %lu > %lu)\n",
507  (unsigned long)FSTART(file, fs),
508  (unsigned long)(fs->data_clusters + 1));
509  MODIFY_START(file, 0, fs);
510  }
511  clusters = prev = 0;
512  for (curr = FSTART(file, fs) ? FSTART(file, fs) :
513  -1; curr != -1; curr = next_cluster(fs, curr)) {
514  FAT_ENTRY curEntry;
515  get_fat(&curEntry, fs->fat, curr, fs);
516 
517  if (!curEntry.value || bad_cluster(fs, curr)) {
518  printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n",
519  path_name(file), curEntry.value ? "bad" : "free", (unsigned long)curr);
520  if (prev)
521  set_fat(fs, prev, -1);
522  else if (!file->offset)
523  die("FAT32 root dir starts with a bad cluster!");
524  else
525  MODIFY_START(file, 0, fs);
526  break;
527  }
528  if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <=
529  clusters * fs->cluster_size) {
530  printf
531  ("%s\n File size is %u bytes, cluster chain length is > %u "
532  "bytes.\n Truncating file to %u bytes.\n", path_name(file),
533  le32toh(file->dir_ent.size),
534  (unsigned)clusters * fs->cluster_size,
535  le32toh(file->dir_ent.size));
536  truncate_file(fs, file, clusters);
537  break;
538  }
539  if ((owner = get_owner(fs, curr))) {
540  int do_trunc = 0;
541  printf("%s and\n", path_name(owner));
542  printf("%s\n share clusters.\n", path_name(file));
543  clusters2 = 0;
544  for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
545  next_cluster(fs, walk))
546  if (walk == curr)
547  break;
548  else {
549  if ((unsigned long long)clusters2 * fs->cluster_size >= UINT32_MAX)
550  die("Internal error: File size is larger than 2^32-1");
551  clusters2++;
552  }
553  restart = file->dir_ent.attr & ATTR_DIR;
554  if (!owner->offset) {
555  printf(" Truncating second to %u bytes because first "
556  "is FAT32 root dir.\n",
557  (unsigned)clusters * fs->cluster_size);
558  do_trunc = 2;
559  } else if (!file->offset) {
560  printf(" Truncating first to %u bytes because second "
561  "is FAT32 root dir.\n",
562  (unsigned)clusters2 * fs->cluster_size);
563  do_trunc = 1;
564  } else {
565  char *trunc_first_string;
566  char *trunc_second_string;
567  char *noninteractive_string;
568 
569  xasprintf(&trunc_first_string,
570  "Truncate first to %u bytes%s",
571  (unsigned)clusters2 * fs->cluster_size,
572  restart ? " and restart" : "");
573  xasprintf(&trunc_second_string,
574  "Truncate second to %u bytes",
575  (unsigned)clusters * fs->cluster_size);
576  xasprintf(&noninteractive_string,
577  " Truncating second to %u bytes.",
578  (unsigned)clusters * fs->cluster_size);
579 
580  do_trunc = get_choice(2, noninteractive_string,
581  2,
582  1, trunc_first_string,
583  2, trunc_second_string);
584 
585  free(trunc_first_string);
586  free(trunc_second_string);
587  free(noninteractive_string);
588  }
589 
590  if (do_trunc == 1) {
591  prev = 0;
592  clusters = 0;
593  for (this = FSTART(owner, fs); this > 0 && this != -1; this =
594  next_cluster(fs, this)) {
595  if (this == curr) {
596  if (prev)
597  set_fat(fs, prev, -1);
598  else
599  MODIFY_START(owner, 0, fs);
600  MODIFY(owner, size, htole32(clusters * fs->cluster_size));
601  if (restart)
602  return 1;
603  while (this > 0 && this != -1) {
604  set_owner(fs, this, NULL);
605  this = next_cluster(fs, this);
606  }
607  this = curr;
608  break;
609  }
610  if ((unsigned long long)clusters * fs->cluster_size >= UINT32_MAX)
611  die("Internal error: File size is larger than 2^32-1");
612  clusters++;
613  prev = this;
614  }
615  if (this != curr)
616  die("Internal error: didn't find cluster %d in chain"
617  " starting at %d", curr, FSTART(owner, fs));
618  } else {
619  if (prev)
620  set_fat(fs, prev, -1);
621  else
622  MODIFY_START(file, 0, fs);
623  break;
624  }
625  }
626  set_owner(fs, curr, file);
627  if ((unsigned long long)clusters * fs->cluster_size >= UINT32_MAX)
628  die("Internal error: File size is larger than 2^32-1");
629  clusters++;
630  prev = curr;
631  }
632  if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) >
633  clusters * fs->cluster_size) {
634  printf
635  ("%s\n File size is %u bytes, cluster chain length is %u bytes."
636  "\n Truncating file to %u bytes.\n", path_name(file),
637  le32toh(file->dir_ent.size),
638  (unsigned)clusters * fs->cluster_size,
639  (unsigned)clusters * fs->cluster_size);
640  MODIFY(file, size,
641  htole32(clusters * fs->cluster_size));
642  }
643  return 0;
644 }
645 
646 static int check_files(DOS_FS * fs, DOS_FILE * start)
647 {
648  while (start) {
649  if (check_file(fs, start))
650  return 1;
651  start = start->next;
652  }
653  return 0;
654 }
655 
656 static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
657 {
658  DOS_FILE *parent, **walk, **scan;
659  int skip, redo;
660  int good, bad;
661 
662  if (!*root)
663  return 0;
664  parent = (*root)->parent;
665  good = bad = 0;
666  for (walk = root; *walk; walk = &(*walk)->next)
667  if (bad_name(*walk))
668  bad++;
669  else
670  good++;
671  if (*root && parent && good + bad > 4 && bad > good / 2) {
672  printf("%s\n Has a large number of bad entries. (%d/%d)\n",
673  path_name(parent), bad, good + bad);
674  if (!dots)
675  printf(" Not dropping root directory.\n");
676  else if (get_choice(2, " Not dropping it in auto-mode.",
677  2,
678  1, "Drop directory",
679  2, "Keep directory") == 1) {
680  truncate_file(fs, parent, 0);
681  MODIFY(parent, name[0], DELETED_FLAG);
682  /* buglet: deleted directory stays in the list. */
683  return 1;
684  }
685  }
686  redo = 0;
687  walk = root;
688  while (*walk) {
689  if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
690  puts(path_name(*walk));
691  printf(" Bad short file name (%s).\n",
692  file_name((*walk)->dir_ent.name));
693  switch (get_choice(3, " Auto-renaming it.",
694  4,
695  1, "Drop file",
696  2, "Rename file",
697  3, "Auto-rename",
698  4, "Keep it")) {
699  case 1:
700  drop_file(fs, *walk);
701  walk = &(*walk)->next;
702  continue;
703  case 2:
704  rename_file(*walk);
705  redo = 1;
706  break;
707  case 3:
708  auto_rename(*walk);
709  printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name));
710  break;
711  case 4:
712  break;
713  }
714  }
715  /* don't check for duplicates of the volume label */
716  if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
717  scan = &(*walk)->next;
718  skip = 0;
719  while (*scan && !skip) {
720  if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
721  !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
722  MSDOS_NAME)) {
723  printf("%s\n Duplicate directory entry.\n First %s\n",
724  path_name(*walk), file_stat(*walk));
725  printf(" Second %s\n", file_stat(*scan));
726  switch (get_choice(6, " Auto-renaming second.",
727  6,
728  1, "Drop first",
729  2, "Drop second",
730  3, "Rename first",
731  4, "Rename second",
732  5, "Auto-rename first",
733  6, "Auto-rename second")) {
734  case 1:
735  drop_file(fs, *walk);
736  *walk = (*walk)->next;
737  skip = 1;
738  break;
739  case 2:
740  drop_file(fs, *scan);
741  *scan = (*scan)->next;
742  continue;
743  case 3:
744  rename_file(*walk);
745  printf(" Renamed to %s\n", path_name(*walk));
746  redo = 1;
747  break;
748  case 4:
749  rename_file(*scan);
750  printf(" Renamed to %s\n", path_name(*walk));
751  redo = 1;
752  break;
753  case 5:
754  auto_rename(*walk);
755  printf(" Renamed to %s\n",
756  file_name((*walk)->dir_ent.name));
757  break;
758  case 6:
759  auto_rename(*scan);
760  printf(" Renamed to %s\n",
761  file_name((*scan)->dir_ent.name));
762  break;
763  }
764  }
765  scan = &(*scan)->next;
766  }
767  if (skip)
768  continue;
769  }
770  if (!redo)
771  walk = &(*walk)->next;
772  else {
773  walk = root;
774  redo = 0;
775  }
776  }
777  return 0;
778 }
779 
780 /**
781  * Check a dentry's cluster chain for bad clusters.
782  * If requested, we verify readability and mark unreadable clusters as bad.
783  *
784  * @param[inout] fs Information about the filesystem
785  * @param[in] file dentry to check
786  * @param[in] read_test Nonzero == verify that dentry's clusters can
787  * be read
788  */
789 static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
790 {
791  DOS_FILE *owner;
792  uint32_t walk, prev, clusters, next_clu;
793 
794  prev = clusters = 0;
795  for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
796  walk = next_clu) {
797  next_clu = next_cluster(fs, walk);
798 
799  /* In this stage we are checking only for a loop within our own
800  * cluster chain.
801  * Cross-linking of clusters is handled in check_file()
802  */
803  if ((owner = get_owner(fs, walk))) {
804  if (owner == file) {
805  printf("%s\n Circular cluster chain. Truncating to %lu "
806  "cluster%s.\n", path_name(file), (unsigned long)clusters,
807  clusters == 1 ? "" : "s");
808  if (prev)
809  set_fat(fs, prev, -1);
810  else if (!file->offset)
811  die("Bad FAT32 root directory! (bad start cluster)\n");
812  else
813  MODIFY_START(file, 0, fs);
814  }
815  break;
816  }
817  if (bad_cluster(fs, walk))
818  break;
819  if (read_test) {
820  if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
821  prev = walk;
822  clusters++;
823  } else {
824  printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n",
825  path_name(file), (unsigned long)clusters, (unsigned long)walk);
826  if (prev)
827  set_fat(fs, prev, next_cluster(fs, walk));
828  else
829  MODIFY_START(file, next_cluster(fs, walk), fs);
830  set_fat(fs, walk, -2);
831  }
832  } else {
833  prev = walk;
834  clusters++;
835  }
836  set_owner(fs, walk, file);
837  }
838  /* Revert ownership (for now) */
839  for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
840  walk = next_cluster(fs, walk))
841  if (bad_cluster(fs, walk))
842  break;
843  else if (get_owner(fs, walk) == file)
844  set_owner(fs, walk, NULL);
845  else
846  break;
847 }
848 
849 static void undelete(DOS_FS * fs, DOS_FILE * file)
850 {
851  uint32_t clusters, left, prev, walk;
852 
853  clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) /
854  fs->cluster_size;
855  prev = 0;
856 
857  walk = FSTART(file, fs);
858 
859  while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) {
860 
861  FAT_ENTRY curEntry;
862  get_fat(&curEntry, fs->fat, walk, fs);
863 
864  if (!curEntry.value)
865  break;
866 
867  left--;
868  if (prev)
869  set_fat(fs, prev, walk);
870  prev = walk;
871  walk++;
872  }
873  if (prev)
874  set_fat(fs, prev, -1);
875  else
876  MODIFY_START(file, 0, fs);
877  if (left)
878  printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
879  (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s");
880 
881 }
882 
883 static void new_dir(void)
884 {
885  lfn_reset();
886 }
887 
888 /**
889  * Create a description for a referenced dentry and insert it in our dentry
890  * tree. Then, go check the dentry's cluster chain for bad clusters and
891  * cluster loops.
892  *
893  * @param[inout] fs Information about the filesystem
894  * @param[out] chain
895  * @param[in] parent Information about parent directory of this file
896  * NULL == no parent ('file' is root directory)
897  * @param[in] offset Partition-relative byte offset of directory entry of interest
898  * 0 == Root directory
899  * @param cp
900  */
901 static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
902  off_t offset, FDSC ** cp)
903 {
904  DOS_FILE *new;
905  DIR_ENT de;
906  FD_TYPE type;
907 
908  if (offset)
909  fs_read(offset, sizeof(DIR_ENT), &de);
910  else {
911  /* Construct a DIR_ENT for the root directory */
912  memset(&de, 0, sizeof de);
913  memcpy(de.name, " ", MSDOS_NAME);
914  de.attr = ATTR_DIR;
915  de.start = htole16(fs->root_cluster & 0xffff);
916  de.starthi = htole16((fs->root_cluster >> 16) & 0xffff);
917  }
918  if ((type = file_type(cp, (char *)de.name)) != fdt_none) {
919  if (type == fdt_undelete && (de.attr & ATTR_DIR))
920  die("Can't undelete directories.");
921  file_modify(cp, (char *)de.name);
922  fs_write(offset, 1, &de);
923  }
924  if (IS_FREE(de.name)) {
926  return;
927  }
928  if (de.attr == VFAT_LN_ATTR) {
929  lfn_add_slot(&de, offset);
930  return;
931  }
932  new = qalloc(&mem_queue, sizeof(DOS_FILE));
933  new->lfn = lfn_get(&de, &new->lfn_offset);
934  new->offset = offset;
935  memcpy(&new->dir_ent, &de, sizeof(de));
936  new->next = new->first = NULL;
937  new->parent = parent;
938  if (type == fdt_undelete)
939  undelete(fs, new);
940  **chain = new;
941  *chain = &new->next;
942  if (list) {
943  printf("Checking file %s", path_name(new));
944  if (new->lfn)
945  printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */
946  printf("\n");
947  }
948  /* Don't include root directory in the total file count */
949  if (offset)
950  ++n_files;
951  test_file(fs, new, test); /* Bad cluster check */
952 }
953 
954 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
955 
956 static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
957 {
958  DOS_FILE **chain;
959  int i;
960  uint32_t clu_num;
961 
962  chain = &this->first;
963  i = 0;
964  clu_num = FSTART(this, fs);
965  new_dir();
966  if (clu_num != 0 && clu_num != -1 && this->offset) {
967  DOS_FILE file;
968 
969  file.lfn = NULL;
970  file.lfn_offset = 0;
971  file.next = NULL;
972  file.parent = this;
973  file.first = NULL;
974 
975  file.offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
976  fs_read(file.offset, sizeof(DIR_ENT), &file.dir_ent);
977  if (handle_dot(fs, &file, 0))
978  return 1;
979  i += sizeof(DIR_ENT);
980 
981  file.offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
982  fs_read(file.offset, sizeof(DIR_ENT), &file.dir_ent);
983  if (handle_dot(fs, &file, 1))
984  return 1;
985  i += sizeof(DIR_ENT);
986  }
987  while (clu_num > 0 && clu_num != -1) {
988  add_file(fs, &chain, this,
989  cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
990  i += sizeof(DIR_ENT);
991  if (!(i % fs->cluster_size))
992  if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
993  break;
994  }
996  if (check_dir(fs, &this->first, this->offset))
997  return 0;
998  if (check_files(fs, this->first))
999  return 1;
1000  return subdirs(fs, this, cp);
1001 }
1002 
1003 /**
1004  * Recursively scan subdirectories of the specified parent directory.
1005  *
1006  * @param[inout] fs Information about the filesystem
1007  * @param[in] parent Identifies the directory to scan
1008  * @param[in] cp
1009  *
1010  * @return 0 Success
1011  * @return 1 Error
1012  */
1013 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
1014 {
1015  DOS_FILE *walk;
1016 
1017  for (walk = parent ? parent->first : root; walk; walk = walk->next)
1018  if (!IS_FREE(walk->dir_ent.name) && (walk->dir_ent.attr & ATTR_DIR))
1019  if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
1020  return 1;
1021  return 0;
1022 }
1023 
1024 /**
1025  * Scan all directory and file information for errors.
1026  *
1027  * @param[inout] fs Information about the filesystem
1028  *
1029  * @return 0 Success
1030  * @return 1 Error
1031  */
1033 {
1034  DOS_FILE **chain;
1035  int i;
1036 
1037  root = NULL;
1038  chain = &root;
1039  new_dir();
1040  if (fs->root_cluster) {
1041  add_file(fs, &chain, NULL, 0, &fp_root);
1042  } else {
1043  for (i = 0; i < fs->root_entries; i++)
1044  add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
1045  &fp_root);
1046  }
1048  (void)check_dir(fs, &root, 0);
1049  if (check_files(fs, root))
1050  return 1;
1051  return subdirs(fs, NULL, &fp_root);
1052 }
1053 
1054 static char print_fat_dirty_state(void)
1055 {
1056  printf("Dirty bit is set. Fs was not properly unmounted and"
1057  " some data may be corrupt.\n");
1058 
1059  return get_choice(1, " Automatically removing dirty bit.",
1060  2,
1061  1, "Remove dirty bit",
1062  2, "No action");
1063 }
1064 
1066 {
1067  if (fs->fat_bits == 32) {
1068  struct boot_sector b32;
1069  FAT_ENTRY fat32_flags;
1070 
1071  get_fat(&fat32_flags, fs->fat, 1, fs);
1072  fs_read(0, sizeof(b32), &b32);
1073 
1074  if ((b32.boot_flags & FAT_STATE_DIRTY) || !(fat32_flags.value & FAT32_FLAG_CLEAN_SHUTDOWN)) {
1075  if (print_fat_dirty_state() == 1) {
1076  if (b32.boot_flags & FAT_STATE_DIRTY) {
1077  b32.boot_flags &= ~FAT_STATE_DIRTY;
1078  fs_write(0, sizeof(b32), &b32);
1079  }
1080  if (!(fat32_flags.value & FAT32_FLAG_CLEAN_SHUTDOWN)) {
1081  uint32_t *new_flags_ptr = (uint32_t *)(fs->fat + 4);
1082  *new_flags_ptr = htole32(fat32_flags.value | FAT32_FLAG_CLEAN_SHUTDOWN | (fat32_flags.reserved << 28));
1083  fs_write(fs->fat_start + 4, 4, new_flags_ptr);
1084  if (fs->nfats > 1)
1085  fs_write(fs->fat_start + 4 + fs->fat_size, 4, new_flags_ptr);
1086  }
1087  }
1088  }
1089  } else {
1090  struct boot_sector_16 b16;
1091  FAT_ENTRY fat16_flags;
1092  int fat16_is_dirty = 0;
1093 
1094  fs_read(0, sizeof(b16), &b16);
1095 
1096  if (fs->fat_bits == 16) {
1097  get_fat(&fat16_flags, fs->fat, 1, fs);
1098  fat16_is_dirty = !(fat16_flags.value & FAT16_FLAG_CLEAN_SHUTDOWN);
1099  }
1100 
1101  if ((b16.boot_flags & FAT_STATE_DIRTY) || fat16_is_dirty) {
1102  if (print_fat_dirty_state() == 1) {
1103  if (b16.boot_flags & FAT_STATE_DIRTY) {
1104  b16.boot_flags &= ~FAT_STATE_DIRTY;
1105  fs_write(0, sizeof(b16), &b16);
1106  }
1107  if (fat16_is_dirty) {
1108  uint16_t *new_flags_ptr = (uint16_t *)(fs->fat + 2);
1109  *new_flags_ptr = htole16(fat16_flags.value | FAT16_FLAG_CLEAN_SHUTDOWN);
1110  fs_write(fs->fat_start + 2, 2, new_flags_ptr);
1111  if (fs->nfats > 1)
1112  fs_write(fs->fat_start + 2 + fs->fat_size, 2, new_flags_ptr);
1113  }
1114  }
1115  }
1116  }
1117 }
1118 
1119 static void get_new_label(char doslabel[12])
1120 {
1121  char newlabel[256];
1122  size_t len;
1123  char *p;
1124  int ret;
1125  int i;
1126 
1127  while (1) {
1128  if (get_line("New label", newlabel, sizeof(newlabel))) {
1129  if ((p = strchr(newlabel, '\n')))
1130  *p = 0;
1131 
1132  len = mbstowcs(NULL, newlabel, 0);
1133  if (len != (size_t)-1 && len > 11) {
1134  printf("Label can be no longer than 11 characters\n");
1135  continue;
1136  }
1137 
1138  if (!local_string_to_dos_string(doslabel, newlabel, 12)) {
1139  printf("Error when processing label\n");
1140  continue;
1141  }
1142 
1143  for (i = strlen(doslabel); i < 11; ++i)
1144  doslabel[i] = ' ';
1145  doslabel[11] = 0;
1146 
1147  ret = validate_volume_label(doslabel);
1148  if ((ret && only_uppercase_label) || (ret & ~0x1)) {
1149  printf("New label is invalid\n");
1150  continue;
1151  } else if (ret & 0x1) {
1152  printf("Warning: lowercase labels might not work properly on some systems\n");
1153  }
1154 
1155  break;
1156  }
1157  }
1158 }
1159 
1160 static int check_boot_label(DOS_FS *fs)
1161 {
1162  char doslabel[12];
1163  wchar_t wlabel[12];
1164  int ret;
1165  int i;
1166 
1167  ret = validate_volume_label(fs->label);
1168  if (ret & ~0x1) {
1169  printf("Label '%s' stored in boot sector is not valid.\n", pretty_label(fs->label));
1170  switch (get_choice(1, " Auto-removing label from boot sector.",
1171  2,
1172  1, "Remove invalid label from boot sector",
1173  2, "Enter new label")) {
1174  case 1:
1175  write_boot_label(fs, "NO NAME ");
1176  memcpy(fs->label, "NO NAME ", 11);
1177  return 1;
1178  case 2:
1179  get_new_label(doslabel);
1180  write_boot_label(fs, doslabel);
1181  memcpy(fs->label, doslabel, 11);
1182  return 1;
1183  }
1184  } else if ((ret & 0x1) && only_uppercase_label) {
1185  printf("Label '%s' stored in boot sector contains lowercase characters.\n", pretty_label(fs->label));
1186  switch (get_choice(1, " Auto-changing lowercase characters to uppercase",
1187  3,
1188  1, "Change lowercase characters to uppercase",
1189  2, "Remove invalid label",
1190  2, "Set new label")) {
1191  case 1:
1192  if (!dos_string_to_wchar_string(wlabel, fs->label, sizeof(wlabel)))
1193  die("Cannot change lowercase characters to uppercase.");
1194  for (i = 0; i < 11; ++i)
1195  wlabel[i] = towupper(wlabel[i]);
1196  if (!wchar_string_to_dos_string(doslabel, wlabel, sizeof(doslabel)))
1197  die("Cannot change lowercase characters to uppercase.");
1198  write_boot_label(fs, doslabel);
1199  memcpy(fs->label, doslabel, 11);
1200  return 1;
1201  case 2:
1202  write_boot_label(fs, "NO NAME ");
1203  memcpy(fs->label, "NO NAME ", 11);
1204  return 1;
1205  case 3:
1206  get_new_label(doslabel);
1207  write_boot_label(fs, doslabel);
1208  memcpy(fs->label, doslabel, 11);
1209  return 1;
1210  }
1211  }
1212 
1213  return 0;
1214 }
1215 
1217 {
1218  DIR_ENT de;
1219  off_t offset;
1220  char buffer[256];
1221  char doslabel[12];
1222  wchar_t wlabel[12];
1223  int ret;
1224  int i;
1225 
1226  offset = find_volume_de(fs, &de);
1227 
1228  if (offset == 0 && memcmp(fs->label, "NO NAME ", 11) != 0)
1229  check_boot_label(fs);
1230 
1231  if (offset == 0 && memcmp(fs->label, "NO NAME ", 11) != 0) {
1232  printf("Label in boot sector is '%s', but there is no volume label in root directory.\n", pretty_label(fs->label));
1233  switch (get_choice(1, " Auto-removing label from boot sector.",
1234  2,
1235  1, "Remove label from boot sector",
1236  2, "Copy label from boot sector to root directory")) {
1237  case 1:
1238  write_boot_label(fs, "NO NAME ");
1239  memcpy(fs->label, "NO NAME ", 11);
1240  break;
1241  case 2:
1242  write_volume_label(fs, fs->label);
1243  offset = find_volume_de(fs, &de);
1244  break;
1245  }
1246  }
1247 
1248  if (offset != 0) {
1249  memcpy(doslabel, de.name, 11);
1250  if (doslabel[0] == 0x05)
1251  doslabel[0] = 0xe5;
1252  ret = validate_volume_label(doslabel);
1253  if (ret & ~0x1) {
1254  printf("Volume label '%s' stored in root directory is not valid.\n", pretty_label(doslabel));
1255  switch (get_choice(1, " Auto-removing label.",
1256  2,
1257  1, "Remove invalid label",
1258  2, "Set new label")) {
1259  case 1:
1260  remove_label(fs);
1261  memcpy(fs->label, "NO NAME ", 11);
1262  offset = 0;
1263  break;
1264  case 2:
1265  get_new_label(doslabel);
1266  write_label(fs, doslabel);
1267  memcpy(fs->label, doslabel, 11);
1268  break;
1269  }
1270  } else if ((ret & 0x1) && only_uppercase_label) {
1271  printf("Volume label '%s' stored in root directory contains lowercase characters.\n", pretty_label(doslabel));
1272  switch (get_choice(1, " Auto-changing lowercase characters to uppercase",
1273  3,
1274  1, "Change lowercase characters to uppercase",
1275  2, "Remove invalid label",
1276  2, "Set new label")) {
1277  case 1:
1278  if (!dos_string_to_wchar_string(wlabel, doslabel, sizeof(wlabel)))
1279  die("Cannot change lowercase characters to uppercase.");
1280  for (i = 0; i < 11; ++i)
1281  wlabel[i] = towupper(wlabel[i]);
1282  if (!wchar_string_to_dos_string(doslabel, wlabel, sizeof(doslabel)))
1283  die("Cannot change lowercase characters to uppercase.");
1284  write_label(fs, doslabel);
1285  memcpy(fs->label, doslabel, 11);
1286  break;
1287  case 2:
1288  remove_label(fs);
1289  memcpy(fs->label, "NO NAME ", 11);
1290  offset = 0;
1291  break;
1292  case 3:
1293  get_new_label(doslabel);
1294  write_label(fs, doslabel);
1295  memcpy(fs->label, doslabel, 11);
1296  break;
1297  }
1298  }
1299  }
1300 
1301 again:
1302 
1303  if (offset != 0 && memcmp(fs->label, "NO NAME ", 11) == 0 && memcmp(doslabel, "NO NAME ", 11) != 0) {
1304  printf("There is no label in boot sector, but there is volume label '%s' stored in root directory\n", pretty_label(doslabel));
1305  switch (get_choice(1, " Auto-copying volume label from root directory to boot sector.",
1306  2,
1307  1, "Copy volume label from root directory to boot sector",
1308  2, "Remove volume label from root directory")) {
1309  case 1:
1310  write_boot_label(fs, doslabel);
1311  memcpy(fs->label, doslabel, 11);
1312  break;
1313  case 2:
1314  remove_label(fs);
1315  offset = 0;
1316  break;
1317  }
1318  }
1319 
1320  if (offset != 0 && memcmp(fs->label, "NO NAME ", 11) != 0 && memcmp(fs->label, doslabel, 11) != 0) {
1321  strncpy(buffer, pretty_label(doslabel), sizeof(buffer)-1);
1322  buffer[sizeof(buffer)-1] = 0;
1323  printf("Volume label '%s' stored in root directory and label '%s' stored in boot sector and different.\n", buffer, pretty_label(fs->label));
1324  switch (get_choice(1, " Auto-copying volume label from root directory to boot sector.",
1325  2,
1326  1, "Copy volume label from root directory to boot sector",
1327  2, "Copy label from boot sector to root directory")) {
1328  case 1:
1329  write_boot_label(fs, doslabel);
1330  memcpy(fs->label, doslabel, 11);
1331  break;
1332  case 2:
1333  ret = check_boot_label(fs);
1334  if (ret)
1335  goto again;
1336  write_volume_label(fs, fs->label);
1337  offset = find_volume_de(fs, &de);
1338  /* NOTE: doslabel is not updated */
1339  break;
1340  }
1341  }
1342 }
void write_label(DOS_FS *fs, char *label)
Definition: boot.c:718
const char * pretty_label(const char *label)
Definition: boot.c:747
off_t find_volume_de(DOS_FS *fs, DIR_ENT *de)
Definition: boot.c:629
void write_volume_label(DOS_FS *fs, char *label)
Definition: boot.c:671
void remove_label(DOS_FS *fs)
Definition: boot.c:729
void write_boot_label(DOS_FS *fs, const char *label)
Definition: boot.c:619
int dos_string_to_wchar_string(wchar_t *out, char *in, unsigned int out_size)
Definition: charconv.c:363
int wchar_string_to_dos_string(char *out, wchar_t *in, unsigned int out_size)
Definition: charconv.c:368
int local_string_to_dos_string(char *out, char *in, unsigned int out_size)
Definition: charconv.c:358
static void add_file(DOS_FS *fs, DOS_FILE ***chain, DOS_FILE *parent, off_t offset, FDSC **cp)
Definition: check.c:901
static void get_new_label(char doslabel[12])
Definition: check.c:1119
static char print_fat_dirty_state(void)
Definition: check.c:1054
static int check_dir(DOS_FS *fs, DOS_FILE **root, int dots)
Definition: check.c:656
#define FSTART(p, fs)
Definition: check.c:54
static void drop_file(DOS_FS *fs, DOS_FILE *file)
Definition: check.c:227
static void rename_file(DOS_FILE *file)
Definition: check.c:301
static int check_boot_label(DOS_FS *fs)
Definition: check.c:1160
static uint32_t scan_free_entry(DOS_FS *fs, DOS_FILE *this)
Definition: check.c:339
static char * path_name(DOS_FILE *file)
Definition: check.c:96
static char * file_stat(DOS_FILE *file)
Definition: check.c:120
#define MODIFY(p, i, v)
Definition: check.c:58
static int scan_dir(DOS_FS *fs, DOS_FILE *this, FDSC **cp)
Definition: check.c:956
static int bad_name(DOS_FILE *file)
Definition: check.c:145
static void new_dir(void)
Definition: check.c:883
int scan_root(DOS_FS *fs)
Definition: check.c:1032
static int subdirs(DOS_FS *fs, DOS_FILE *parent, FDSC **cp)
Definition: check.c:1013
static DOS_FILE * root
Definition: check.c:51
static int check_file(DOS_FS *fs, DOS_FILE *file)
Definition: check.c:450
#define PATH_NAME_MAX
Definition: check.c:49
static void truncate_file(DOS_FS *fs, DOS_FILE *file, uint32_t clusters)
Definition: check.c:237
void check_label(DOS_FS *fs)
Definition: check.c:1216
static void test_file(DOS_FS *fs, DOS_FILE *file, int read_test)
Definition: check.c:789
static void auto_rename(DOS_FILE *file)
Definition: check.c:255
#define MODIFY_START(p, v, fs)
Definition: check.c:67
void check_dirty_bits(DOS_FS *fs)
Definition: check.c:1065
static void lfn_remove(off_t from, off_t to)
Definition: check.c:211
static const char * month_str[]
Definition: check.c:117
static int handle_dot(DOS_FS *fs, DOS_FILE *file, int dotdot)
Definition: check.c:361
static void undelete(DOS_FS *fs, DOS_FILE *file)
Definition: check.c:849
static int check_files(DOS_FS *fs, DOS_FILE *start)
Definition: check.c:646
void die(const char *msg,...)
Definition: common.c:53
int xasprintf(char **strp, const char *fmt,...)
Definition: common.c:141
int validate_volume_label(char *doslabel)
Definition: common.c:324
int atari_format
Definition: common.c:44
void * qalloc(void **root, int size)
Definition: common.c:91
int get_choice(int noninteractive_result, const char *noninteractive_msg, int choices,...)
Definition: common.c:157
char * get_line(const char *prompt, char *dest, size_t length)
Definition: common.c:245
uint32_t next_cluster(DOS_FS *fs, uint32_t cluster)
Definition: fat.c:355
void set_owner(DOS_FS *fs, uint32_t cluster, DOS_FILE *owner)
Definition: fat.c:383
int bad_cluster(DOS_FS *fs, uint32_t cluster)
Definition: fat.c:337
off_t cluster_start(DOS_FS *fs, uint32_t cluster)
Definition: fat.c:368
DOS_FILE * get_owner(DOS_FS *fs, uint32_t cluster)
Definition: fat.c:394
void get_fat(FAT_ENTRY *entry, void *fat, uint32_t cluster, DOS_FS *fs)
Definition: fat.c:48
void set_fat(DOS_FS *fs, uint32_t cluster, int32_t new)
Definition: fat.c:268
int test
Definition: fatlabel.c:49
int list
Definition: fatlabel.c:49
int no_spaces_in_sfns
Definition: fatlabel.c:49
void * mem_queue
Definition: fatlabel.c:52
unsigned n_files
Definition: fatlabel.c:51
void file_modify(FDSC **curr, char *fixed)
Definition: file.c:244
FDSC * fp_root
Definition: file.c:39
int file_cvt(unsigned char *name, unsigned char *fixed)
Definition: file.c:95
FDSC ** file_cd(FDSC **curr, char *fixed)
Definition: file.c:200
char * file_name(unsigned char *fixed)
Definition: file.c:63
FD_TYPE file_type(FDSC **curr, char *fixed)
Definition: file.c:235
FD_TYPE
Definition: file.h:28
@ fdt_none
Definition: file.h:28
@ fdt_undelete
Definition: file.h:28
int only_uppercase_label
Definition: fsck.fat.c:53
#define FAT32_FLAG_CLEAN_SHUTDOWN
Definition: fsck.fat.h:48
#define FAT_STATE_DIRTY
Definition: fsck.fat.h:41
#define VFAT_LN_ATTR
Definition: fsck.fat.h:39
#define FAT16_FLAG_CLEAN_SHUTDOWN
Definition: fsck.fat.h:45
#define FAT_NO_83NAME
Definition: fsck.fat.h:198
int fs_test(off_t pos, int size)
Definition: io.c:101
void fs_read(off_t pos, int size, void *data)
Definition: io.c:78
void fs_write(off_t pos, int size, void *data)
Definition: io.c:114
void lfn_add_slot(DIR_ENT *de, off_t dir_offset)
Definition: lfn.c:200
void lfn_check_orphaned(void)
Definition: lfn.c:512
void lfn_reset(void)
Definition: lfn.c:187
char * lfn_get(DIR_ENT *de, off_t *lfn_offset)
Definition: lfn.c:399
#define MSDOS_DOTDOT
Definition: msdos_fs.h:46
#define MSDOS_DOT
Definition: msdos_fs.h:45
#define DELETED_FLAG
Definition: msdos_fs.h:41
#define ATTR_DIR
Definition: msdos_fs.h:35
#define IS_FREE(n)
Definition: msdos_fs.h:42
#define ATTR_VOLUME
Definition: msdos_fs.h:34
#define MSDOS_NAME
Definition: msdos_fs.h:44
uint16_t time
Definition: fsck.fat.h:138
uint16_t start
Definition: fsck.fat.h:138
uint16_t starthi
Definition: fsck.fat.h:137
uint32_t size
Definition: fsck.fat.h:139
uint8_t lcase
Definition: fsck.fat.h:132
uint8_t attr
Definition: fsck.fat.h:131
uint16_t date
Definition: fsck.fat.h:138
uint8_t name[11]
Definition: fsck.fat.h:130
unsigned int fat_bits
Definition: fsck.fat.h:161
unsigned int cluster_size
Definition: fsck.fat.h:167
unsigned int fat_size
Definition: fsck.fat.h:160
int nfats
Definition: fsck.fat.h:158
off_t fat_start
Definition: fsck.fat.h:159
uint32_t data_clusters
Definition: fsck.fat.h:168
unsigned int root_entries
Definition: fsck.fat.h:165
uint32_t root_cluster
Definition: fsck.fat.h:163
unsigned char * fat
Definition: fsck.fat.h:172
off_t root_start
Definition: fsck.fat.h:164
char label[11]
Definition: fsck.fat.h:175
Definition: fsck.fat.h:152
uint32_t value
Definition: fsck.fat.h:153
struct _dos_file * next
Definition: fsck.fat.h:148
struct _dos_file * parent
Definition: fsck.fat.h:147
DIR_ENT dir_ent
Definition: fsck.fat.h:143
off_t offset
Definition: fsck.fat.h:145
off_t lfn_offset
Definition: fsck.fat.h:146
char * lfn
Definition: fsck.fat.h:144
struct _dos_file * first
Definition: fsck.fat.h:149
Definition: file.h:30
uint8_t boot_flags
Definition: fsck.fat.h:108
uint8_t boot_flags
Definition: fsck.fat.h:79