1/* 2 * Copyright (c) 2005 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#include <sys/stat.h> 25#include <sys/param.h> 26#include <sys/time.h> 27 28#include <err.h> 29#include <errno.h> 30#include <fcntl.h> 31#include <fstab.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36 37#include <sys/types.h> 38#include <sys/ioctl.h> 39#include <sys/disk.h> 40 41#define E_OPENDEV -1 42#define E_READ -5 43 44/* 45 * We don't have a (non-C++) standard header for UDF (yet?), so 46 * let's define the basic structures and constants we'll be using. 47 */ 48 49typedef struct UDFVolumeSequenceDescriptor { 50 unsigned char type; 51 unsigned char id[5]; 52 unsigned char version; 53 unsigned char data[2041]; 54} udfVSD; 55 56#define UDFSTART (32*1024) /* First 32k is unused */ 57 58void usage(void); 59char *rawname(char *name); 60char *unrawname(char *name); 61int CheckUDF(int, int); 62char *blockcheck(char *origname); 63 64char *progname; 65 66/* 67 * prefer to use raw device. TODO: suppose block device is valid but 68 * the corresponding raw device is not valid, then we fail. this is 69 * probably no the desired behavior. 70 */ 71 72int 73main(int argc, char **argv) 74{ 75 char *devname = NULL; 76 int fd, retval; 77 78 retval = 0; 79 fd = -1; 80 81 if ((progname = strrchr(*argv, '/'))) 82 ++progname; 83 else 84 progname = *argv; 85 86 if (argc != 2) { 87 usage(); 88 } else { 89 devname = blockcheck(argv[1]); 90 if (devname != NULL) { 91 if ((fd = open(devname, O_RDONLY, 0)) < 0) { 92 retval = E_OPENDEV; 93 } else { 94 int bsize; 95 if (ioctl(fd, DKIOCGETBLOCKSIZE, (char*)&bsize) == -1) { 96#ifdef DEBUG 97 warn("DKIOCGETBLOCKSIZE ioctl failed for %s", devname); 98#endif 99 bsize = 2048; /* A standard default size */ 100 } 101 retval = CheckUDF(fd, bsize) == 1; 102 if (retval == 0 && bsize != 2048) { 103 retval = CheckUDF(fd, 2048) == 1; 104 } 105 } 106 } 107 } 108 109 return retval; 110} 111 112static int 113IsVSD(udfVSD *vsd) { 114 int retval = memcmp(vsd->id, "BEA01", 5)==0 || 115 memcmp(vsd->id, "BOOT2", 5)==0 || 116 memcmp(vsd->id, "NSR02", 5)==0 || 117 memcmp(vsd->id, "NSR03", 5)==0 || 118 memcmp(vsd->id, "TEA01", 5)==0 || 119 memcmp(vsd->id, "CDW02", 5)==0 || 120 memcmp(vsd->id, "CD001", 5)==0; 121#ifdef DEBUG 122 fprintf(stderr, "IsVSD: Returning %d\n", retval); 123#endif 124 return retval; 125} 126 127/* 128 * This is inspired by the udf25 kext code. 129 * It concludes that a device has a UDF filesystem 130 * if: 131 * 1) It has a Volume Sequence Descriptor; 132 * 2) That VSD has a "BEA01" in it; 133 * 3) That VSD has an "NSR02" or "NSR03" in it before the terminating one. 134 * 135 * It may be necessary to check the AVDP(s), as well. 136 */ 137 138int 139CheckUDF(int fd, int blockSize) { 140 ssize_t err; 141 char buf[blockSize]; 142 off_t curr, max; 143 char found = 0; 144 145 curr = UDFSTART; 146 max = curr + (512 * blockSize); 147 if (lseek(fd, curr, SEEK_SET) == -1) { 148 warn("Cannot seek to %llu", curr); 149 return -1; 150 } 151 152 while (curr < max) { 153 udfVSD *vsd; 154 err = read(fd, buf, sizeof(buf)); 155 if (err != sizeof(buf)) { 156 if (err == -1) { 157 warn("Cannot read %zu bytes", sizeof(buf)); 158 } else { 159 warn("Cannot read %zd bytes, only read %zd", sizeof(buf), err); 160 } 161 return -1; 162 } 163 vsd = (udfVSD*)buf; 164 if (!IsVSD(vsd)) { 165 break; 166 } 167 if (vsd->type == 0 && 168 memcmp(vsd->id, "BEA01", 5) == 0 && 169 vsd->version == 1) { 170 found = 1; 171 break; 172 } 173 curr += blockSize; 174 } 175 if (found == 0) 176 return 0; 177 178 found = 0; 179 180 while (curr < max) { 181 udfVSD *vsd; 182 err = read(fd, buf, sizeof(buf)); 183 if (err != sizeof(buf)) { 184 if (err == -1) { 185 warn("Cannot read %zu bytes", sizeof(buf)); 186 } else { 187 warn("Cannot read %zu bytes, only read %zd", sizeof(buf), err); 188 } 189 return -1; 190 } 191 vsd = (udfVSD*)buf; 192 if (!IsVSD(vsd)) { 193 break; 194 } 195 if (vsd->type == 0 && 196 memcmp(vsd->id, "TEA01", 5) == 0 && 197 vsd->version == 1) { 198 /* we're at the end */ 199 break; 200 } else if (memcmp(vsd->id, "NSR02", 5) == 0 || 201 memcmp(vsd->id, "NSR03", 5) == 0) { 202 found = 1; 203 break; 204 } 205 curr += blockSize; 206 } 207 208 return found; 209} 210 211void 212usage(void) 213{ 214 fprintf(stdout, "usage: %s device\n", progname); 215 return; 216} 217 218/* copied from diskdev_cmds/fsck_hfs/utilities.c */ 219char * 220rawname(char *name) 221{ 222 static char rawbuf[32]; 223 char *dp; 224 225 if ((dp = strrchr(name, '/')) == 0) 226 return (0); 227 *dp = 0; 228 (void) strcpy(rawbuf, name); 229 *dp = '/'; 230 (void) strcat(rawbuf, "/r"); 231 (void) strcat(rawbuf, &dp[1]); 232 233 return (rawbuf); 234} 235 236/* copied from diskdev_cmds/fsck_hfs/utilities.c */ 237char * 238unrawname(char *name) 239{ 240 char *dp; 241 struct stat stb; 242 243 if ((dp = strrchr(name, '/')) == 0) 244 return (name); 245 if (stat(name, &stb) < 0) 246 return (name); 247 if ((stb.st_mode & S_IFMT) != S_IFCHR) 248 return (name); 249 if (dp[1] != 'r') 250 return (name); 251 (void) strcpy(&dp[1], &dp[2]); 252 253 return (name); 254} 255 256/* 257 * copied from diskdev_cmds/fsck_hfs/utilities.c, and modified: 258 * 1) remove "hotroot" 259 * 2) if error, return NULL 260 * 3) if not a char device, return NULL (effectively, this is treated 261 * as error even if accessing the block device might have been OK) 262 */ 263char * 264blockcheck(char *origname) 265{ 266 struct stat stblock, stchar; 267 char *newname, *raw; 268 int retried = 0; 269 270 newname = origname; 271retry: 272 if (stat(newname, &stblock) < 0) { 273 perror(newname); 274 fprintf(stderr, "Can't stat %s\n", newname); 275 return NULL; 276 } 277 if ((stblock.st_mode & S_IFMT) == S_IFBLK) { 278 raw = rawname(newname); 279 if (stat(raw, &stchar) < 0) { 280 perror(raw); 281 fprintf(stderr, "Can't stat %s\n", raw); 282 return NULL; 283 } 284 if ((stchar.st_mode & S_IFMT) == S_IFCHR) { 285 return (raw); 286 } else { 287 fprintf(stderr, "%s is not a character device\n", raw); 288 return NULL; 289 } 290 } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { 291 newname = unrawname(newname); 292 retried++; 293 goto retry; 294 } 295#ifdef DEBUG 296 else if ((stblock.st_mode & S_IFMT) == S_IFREG) { 297 return strdup(origname); 298 } 299#endif 300 /* not a block or character device */ 301 return NULL; 302} 303 304 305