1/* 2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 25 * Copyright (c) 1995 Martin Husemann 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in the 34 * documentation and/or other materials provided with the distribution. 35 * 3. All advertising materials mentioning features or use of this software 36 * must display the following acknowledgement: 37 * This product includes software developed by Martin Husemann 38 * and Wolfgang Solfrank. 39 * 4. Neither the name of the University nor the names of its contributors 40 * may be used to endorse or promote products derived from this software 41 * without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 44 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 45 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 46 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 47 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 48 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 52 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 */ 54 55 56#include <sys/cdefs.h> 57#include <sys/stat.h> 58 59#include <stdlib.h> 60#include <string.h> 61#include <ctype.h> 62#include <stdio.h> 63#include <unistd.h> 64#include <fcntl.h> 65 66#include "ext.h" 67#include "fsutil.h" 68 69int checkfilesys(const char *fname) 70{ 71 int dosfs = -1; 72 struct bootblock boot; 73 int finish_dosdirsection=0; 74 int mod = 0; 75 int ret = FSERROR; 76 int tryFatalAgain = 1; 77 int tryErrorAgain = 3; 78 int tryOthersAgain = 3; 79 char raw_path[64]; 80 81 /* 82 * We accept device paths of the form "/dev/disk6s1" or "disk6s1" 83 * and convert them to the raw path "/dev/rdisk6s1". 84 * 85 * On iOS, we want to be able to run fsck_msdos as user "mobile" 86 * so that it can be spawned from userfsd, which also runs as "mobile". 87 * But since /dev/disk entries are owned by root, we can't open them 88 * directly. userfsd has already used an SPI to open the disk device, 89 * so it will spawn fsck_msdos with that open file descriptor, and use 90 * a path of the form "/dev/fd/NUMBER". 91 * 92 * Unfortunately, iOS doesn't support /dev/fd/ (and if it did, sandboxing 93 * might not allow us to access it). If we can search in /dev/fd, then 94 * we'll just try to open the path as-is. Otherwise, we'll parse the file 95 * descriptor number from the path and use that descriptor directly 96 * (without going through open). 97 */ 98 if (!strncmp(fname, "/dev/disk", 9)) 99 { 100 if (snprintf(raw_path, sizeof(raw_path), "/dev/r%s", &fname[5]) < sizeof(raw_path)) 101 fname = raw_path; 102 /* Else we just use the non-raw disk */ 103 } 104 else if (!strncmp(fname, "disk", 4)) 105 { 106 if (snprintf(raw_path, sizeof(raw_path), "/dev/r%s", fname) < sizeof(raw_path)) 107 fname = raw_path; 108 /* Else the open below is likely to fail */ 109 } 110 else if (!strncmp(fname, "/dev/fd/", 8)) 111 { 112 /* 113 * If /dev/fd/ doesn't exist, or we can't access it, 114 * then parse the file descriptor ourselves. 115 */ 116 if (access("/dev/fd", X_OK)) 117 { 118 char *end_ptr; 119 dosfs = (int)strtol(&fname[8], &end_ptr, 10); 120 if (*end_ptr) 121 { 122 // TODO: is errx or err the right way to report the errors here? 123 pfatal("Invalid file descriptor path: %s", fname); 124 return 8; 125 } 126 127 struct stat info; 128 if (fstat(dosfs, &info)) 129 { 130 perr("Cannot stat"); 131 return 8; 132 } 133 } 134 } 135 136 rdonly = alwaysno || quick; 137 if (!preen) 138 printf("** %s", fname); 139 140 if (dosfs < 0) 141 dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR | O_EXLOCK, 0); 142 if (dosfs < 0 && !rdonly) { 143 dosfs = open(fname, O_RDONLY, 0); 144 if (dosfs >= 0) 145 pwarn(" (NO WRITE)\n"); 146 else if (!preen) 147 printf("\n"); 148 rdonly = 1; 149 } else if (!preen) 150 printf("\n"); 151 152 if (dosfs < 0) { 153 perr("Can't open"); 154 return 8; 155 } 156 157 mod = readboot(dosfs, &boot); 158 if (mod & FSFATAL) { 159 close(dosfs); 160 return 8; 161 } 162 163 if (quick) { 164 /* 165 * FAT12 volumes don't have a dirty bit. If we were asked for 166 * a quick check, then actually do a full scan without repairs. 167 */ 168 if (boot.ClustMask == CLUST12_MASK) { 169 /* Don't attempt to do any repairs */ 170 rdonly = 1; 171 alwaysno = 1; 172 alwaysyes = 0; 173 quiet = 1; 174 175 /* Go verify the volume */ 176 goto Again; 177 } 178 else if (isdirty(dosfs, &boot, boot.ValidFat >= 0 ? boot.ValidFat : 0)) { 179 pwarn("FILESYSTEM DIRTY; SKIPPING CHECKS\n"); 180 ret = FSDIRTY; 181 } 182 else { 183 pwarn("FILESYSTEM CLEAN; SKIPPING CHECKS\n"); 184 ret = 0; 185 } 186 187 close(dosfs); 188 return ret; 189 } 190 191Again: 192 boot.NumFiles = 0; /* Reset file count in case we loop back here */ 193 boot.NumBad = 0; 194 195 /* 196 * [2771127] When there was no active FAT, this code used to 197 * compare FAT #0 with all the other FATs. That doubled the 198 * already large memory usage, and doesn't seem very useful. 199 * Microsoft's specification says the purpose of the alternate 200 * FATs is in case a sector goes bad in the main FAT. In fact, 201 * a Windows 2000 system never even notices the FATs have 202 * different values. Besides, the filesystem itself only ever 203 * uses FAT #0. 204 */ 205 if (!preen && !quiet) { 206 printf("** Phase 1 - Preparing FAT\n"); 207 } 208 209 mod |= fat_init(dosfs, &boot); 210 211 if (mod & FSFATAL) { 212 close(dosfs); 213 return FSERROR; 214 } 215 216 if (!preen && !quiet) 217 printf("** Phase 2 - Checking Directories\n"); 218 219 mod |= resetDosDirSection(&boot); 220 finish_dosdirsection = 1; 221 if (mod & FSFATAL) 222 goto out; 223 /* delay writing FATs */ 224 225 mod |= handleDirTree(dosfs, &boot); 226 if (mod & FSFATAL) 227 goto out; 228 229 if (!preen && !quiet) 230 printf("** Phase 3 - Checking for Orphan Clusters\n"); 231 232 /* 233 * Should we skip this if (mod & FSERROR)? It would be bad to free clusters 234 * that are actually referenced by some directory entry, even if they are 235 * beyond the logical file size (and the file size was not repaired). 236 */ 237 mod |= fat_free_unused(); 238 if (mod & FSFATAL) 239 goto out; 240 241 if (quick) { 242 if (mod) { 243 printf("FILESYSTEM DIRTY\n"); 244 ret = FSDIRTY; 245 } 246 else { 247 printf("FILESYSTEM CLEAN\n"); 248 ret = 0; 249 } 250 } 251 252 if (boot.NumBad) 253 pwarn("%d files, %lld KiB free (%d clusters), %lld KiB bad (%d clusters)\n", 254 boot.NumFiles, 255 (long long) boot.NumFree * (long long) boot.ClusterSize / 1024LL, boot.NumFree, 256 (long long) boot.NumBad * (long long) boot.ClusterSize / 1024LL, boot.NumBad); 257 else 258 pwarn("%d files, %lld KiB free (%d clusters)\n", 259 boot.NumFiles, 260 (long long) boot.NumFree * (long long) boot.ClusterSize / 1024LL, boot.NumFree); 261 262 /* 263 * If we repaired everything, offer to mark the file system clean. 264 */ 265 if (mod && (mod & FSERROR) == 0) { 266 if (mod & FSDIRTY) { 267 if (ask(1, "MARK FILE SYSTEM CLEAN") == 0) 268 mod &= ~FSDIRTY; 269 270 if (mod & FSDIRTY) { 271 pwarn("MARKING FILE SYSTEM CLEAN\n"); 272 mod |= fat_mark_clean(); 273 } else { 274 pwarn("\n***** FILE SYSTEM IS LEFT MARKED AS DIRTY *****\n"); 275 } 276 } 277 } 278 279 /* 280 * We're done changing the FAT, so flush any changes. 281 */ 282 mod |= fat_flush(); 283 284 /* Don't bother trying multiple times if we're not doing repairs */ 285 if (mod && rdonly) 286 goto out; 287 288 if (((mod & FSFATAL) && (--tryFatalAgain > 0)) || 289 ((mod & FSERROR) && (--tryErrorAgain > 0)) || 290 ((mod & FSFIXFAT) && (--tryOthersAgain > 0))) { 291 mod = 0; 292 goto Again; 293 } 294 if ((mod & (FSFATAL | FSERROR)) == 0) 295 ret = 0; 296 297 out: 298 if (finish_dosdirsection) 299 finishDosDirSection(); 300 fat_uninit(); 301 freeUseMap(); 302 close(dosfs); 303 304 if (mod & (FSFATMOD|FSDIRMOD)) 305 pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 306 307 return ret; 308} 309