/filesystems/unixfs/ancientfs/ancientfs_dtp.c
C | 515 lines | 415 code | 87 blank | 13 comment | 86 complexity | 1feb032bec07b3ae11d7cb49c35bd455 MD5 | raw file
1/* 2 * Ancient UNIX File Systems for MacFUSE 3 * Amit Singh 4 * http://osxbook.com 5 */ 6 7#include "ancientfs_dtp.h" 8#include "unixfs_common.h" 9 10#include <errno.h> 11#include <fcntl.h> 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15#include <unistd.h> 16#include <sys/ioctl.h> 17#include <sys/stat.h> 18 19DECL_UNIXFS("UNIX dtp", dtp); 20 21static void* 22unixfs_internal_init(const char* dmg, uint32_t flags, fs_endian_t fse, 23 char** fsname, char** volname) 24{ 25 int fd = -1; 26 if ((fd = open(dmg, O_RDONLY)) < 0) { 27 perror("open"); 28 return NULL; 29 } 30 31 int err, i, j; 32 struct stat stbuf; 33 struct super_block* sb = (struct super_block*)0; 34 struct filsys* fs = (struct filsys*)0; 35 uint32_t tapedir_begin_block = 0, tapedir_end_block = 0, last_block = 0; 36 37 if ((err = fstat(fd, &stbuf)) != 0) { 38 perror("fstat"); 39 goto out; 40 } 41 42 if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) { 43 err = EINVAL; 44 fprintf(stderr, "%s is not a tape image file\n", dmg); 45 goto out; 46 } 47 48 if (flags & ANCIENTFS_DECTAPE) { 49 tapedir_begin_block = 0; 50 tapedir_end_block = TAPEDIR_END_BLOCK_DEC; 51 } else if (flags & ANCIENTFS_MAGTAPE) { 52 tapedir_begin_block = 0; 53 tapedir_end_block = TAPEDIR_END_BLOCK_MAG; 54 } else if (flags & ANCIENTFS_GENTAPE) { 55 tapedir_begin_block = 0; 56 tapedir_end_block = TAPEDIR_END_BLOCK_GENERIC; 57 } else { 58 err = EINVAL; 59 fprintf(stderr, "unrecognized tape type\n"); 60 goto out; 61 } 62 63 if (S_ISREG(stbuf.st_mode)) 64 last_block = stbuf.st_size / BSIZE; 65 else 66 last_block = tapedir_end_block; 67 68 sb = malloc(sizeof(struct super_block)); 69 if (!sb) { 70 err = ENOMEM; 71 goto out; 72 } 73 74 assert(sizeof(struct filsys) <= BSIZE); 75 76 fs = calloc(1, BSIZE); 77 if (!fs) { 78 free(sb); 79 err = ENOMEM; 80 goto out; 81 } 82 83 unixfs = sb; 84 85 unixfs->s_flags = flags; 86 unixfs->s_endian = (fse == UNIXFS_FS_INVALID) ? UNIXFS_FS_PDP : fse; 87 unixfs->s_fs_info = (void*)fs; 88 unixfs->s_bdev = fd; 89 90 /* must initialize the inode layer before sanity checking */ 91 if ((err = unixfs_inodelayer_init(sizeof(struct tap_node_info))) != 0) 92 goto out; 93 94 struct inode* rootip = unixfs_inodelayer_iget((ino_t)ROOTINO); 95 if (!rootip) { 96 fprintf(stderr, "*** fatal error: no root inode\n"); 97 abort(); 98 } 99 100 rootip->I_mode = S_IFDIR | 0755; 101 rootip->I_uid = getuid(); 102 rootip->I_gid = getgid(); 103 rootip->I_size = 2; 104 rootip->I_atime_sec = rootip->I_mtime_sec = rootip->I_ctime_sec = time(0); 105 106 struct tap_node_info* rootti = (struct tap_node_info*)rootip->I_private; 107 rootti->ti_self = rootip; 108 rootti->ti_name[0] = '\0'; 109 rootti->ti_parent = NULL; 110 rootti->ti_children = NULL; 111 rootti->ti_next_sibling = NULL; 112 113 unixfs_inodelayer_isucceeded(rootip); 114 115 fs->s_fsize = stbuf.st_size / BSIZE; 116 fs->s_files = 0; 117 fs->s_directories = 1 + 1 + 1; 118 fs->s_rootip = rootip; 119 fs->s_lastino = ROOTINO; 120 121 char tapeblock[BSIZE]; 122 123 for (i = tapedir_begin_block; i < tapedir_end_block; i++) { 124 125 if (pread(fd, tapeblock, BSIZE, (off_t)(i * BSIZE)) != BSIZE) { 126 fprintf(stderr, "*** fatal error: cannot read tape block %llu\n", 127 (off_t)i); 128 err = EIO; 129 goto out; 130 } 131 132 struct dinode_dtp* di = (struct dinode_dtp*)tapeblock; 133 int inopb = INOPB; 134 135 if (i == 0) { /* special case block 0 */ 136 uint8_t* doff = (uint8_t*)tapeblock; 137 fs->s_dataoffset = 64 * (256 * doff[6] + doff[7]); 138 di = (struct dinode_dtp*)((char*)tapeblock + 128); 139 inopb--; 140 } else if ((i * BSIZE) >= fs->s_dataoffset) { 141 i = tapedir_end_block; 142 j = inopb; 143 break; 144 } 145 146 for (j = 0; j < inopb; j++, di++) { 147 148 /* no checksum? */ 149 150 if (!di->di_path[0]) { 151 if (flags & ANCIENTFS_GENTAPE) { 152 i = tapedir_end_block; 153 j = inopb; 154 } 155 continue; 156 } 157 158 ino_t parent_ino = ROOTINO; 159 160 char* path = (char*)di->di_path; 161 162 size_t pathlen = strlen(path); 163 if ((*path == '.') && ((pathlen == 1) || 164 ((pathlen == 2) && (*(path + 1) == '/')))) { 165 /* root */ 166 rootip->I_mode = fs16_to_host(unixfs->s_endian, di->di_mode); 167 rootip->I_atime_sec = \ 168 rootip->I_mtime_sec = \ 169 rootip->I_ctime_sec = \ 170 fs32_to_host(unixfs->s_endian, di->di_mtime); 171 continue; 172 } 173 174 /* we don't deal with many fancy paths here: just '/' and './' */ 175 if (*path == '/') 176 path++; 177 else if (*path == '.' && *(path + 1) == '/') 178 path += 2; 179 180 char *cnp, *term; 181 182 for (cnp = strtok_r(path, "/", &term); cnp; 183 cnp = strtok_r(NULL, "/", &term)) { 184 /* we have { parent_ino, cnp } */ 185 struct stat stbuf; 186 int missing = unixfs_internal_namei(parent_ino, cnp, &stbuf); 187 if (!missing) { 188 parent_ino = stbuf.st_ino; 189 continue; 190 } 191 struct inode* ip = 192 unixfs_inodelayer_iget((ino_t)(fs->s_lastino + 1)); 193 if (!ip) { 194 fprintf(stderr, "*** fatal error: no inode for %llu\n", 195 (ino64_t)(fs->s_lastino + 1)); 196 abort(); 197 } 198 ip->I_mode = fs16_to_host(unixfs->s_endian, di->di_mode); 199 ip->I_uid = di->di_uid; 200 ip->I_gid = di->di_gid; 201 ip->I_size = di->di_size0 << 16 | 202 fs16_to_host(unixfs->s_endian, di->di_size1); 203 ip->I_daddr[0] = (uint32_t)fs16_to_host(unixfs->s_endian, 204 di->di_addr); 205 206 ip->I_nlink = 1; 207 ip->I_atime_sec = ip->I_mtime_sec = ip->I_ctime_sec = 208 fs32_to_host(unixfs->s_endian, di->di_mtime); 209 struct tap_node_info* ti = (struct tap_node_info*)ip->I_private; 210 memcpy(ti->ti_name, cnp, strlen(cnp)); 211 ti->ti_self = ip; 212 ti->ti_children = NULL; 213 /* this should work out as long as we have no corruption */ 214 struct inode* parent_ip = unixfs_internal_iget(parent_ino); 215 parent_ip->I_size += 1; 216 ti->ti_parent = (struct tap_node_info*)(parent_ip->I_private); 217 ti->ti_next_sibling = ti->ti_parent->ti_children; 218 ti->ti_parent->ti_children = ti; 219 if (S_ISDIR(ancientfs_dtp_mode(ip->I_mode, flags))) { 220 fs->s_directories++; 221 parent_ino = fs->s_lastino + 1; 222 ip->I_size = 2; 223 ip->I_daddr[0] = 0; 224 } else 225 fs->s_files++; 226 fs->s_lastino++; 227 unixfs_internal_iput(parent_ip); 228 unixfs_inodelayer_isucceeded(ip); 229 /* no put */ 230 } 231 } 232 } 233 234 unixfs->s_statvfs.f_bsize = BSIZE; 235 unixfs->s_statvfs.f_frsize = BSIZE; 236 unixfs->s_statvfs.f_ffree = 0; 237 unixfs->s_statvfs.f_files = fs->s_files + fs->s_directories; 238 unixfs->s_statvfs.f_blocks = fs->s_fsize; 239 unixfs->s_statvfs.f_bfree = 0; 240 unixfs->s_statvfs.f_bavail = 0; 241 unixfs->s_dentsize = 1; 242 unixfs->s_statvfs.f_namemax = DIRSIZ; 243 244 snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "UNIX dtp"); 245 246 char* dmg_basename = basename((char*)dmg); 247 snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (tape=%s)", 248 unixfs_fstype, (dmg_basename) ? dmg_basename : "Tape Image"); 249 250 *fsname = unixfs->s_fsname; 251 *volname = unixfs->s_volname; 252 253out: 254 if (err) { 255 if (fd >= 0) 256 close(fd); 257 if (fs) 258 free(fs); 259 if (sb) 260 free(sb); 261 return NULL; 262 } 263 264 return sb; 265} 266 267static void 268unixfs_internal_fini(void* filsys) 269{ 270 struct super_block* sb = (struct super_block*)filsys; 271 struct filsys* fs = (struct filsys*)sb->s_fs_info; 272 ino_t i = fs->s_lastino; 273 for (; i >= ROOTINO; i--) { 274 struct inode* tmp = unixfs_internal_iget(i); 275 if (tmp) { 276 unixfs_internal_iput(tmp); 277 unixfs_internal_iput(tmp); 278 } 279 } 280 281 unixfs_inodelayer_fini(); 282 283 if (sb) { 284 if (sb->s_bdev >= 0) 285 close(sb->s_bdev); 286 sb->s_bdev = -1; 287 if (sb->s_fs_info) 288 free(sb->s_fs_info); 289 } 290} 291 292static off_t 293unixfs_internal_alloc(void) 294{ 295 return (off_t)0; 296} 297 298static off_t 299unixfs_internal_bmap(struct inode* ip, off_t lblkno, int* error) 300{ 301 off_t nblocks = (off_t)((ip->I_size + (BSIZE - 1)) / BSIZE); 302 303 if (lblkno >= nblocks) { 304 *error = EFBIG; 305 return (off_t)0; 306 } 307 308 return (off_t)(ip->I_daddr[0] + lblkno + 309 (((struct filsys*)unixfs->s_fs_info)->s_dataoffset / BSIZE)); 310} 311 312static int 313unixfs_internal_bread(off_t blkno, char* blkbuf) 314{ 315 if (blkno >= ((struct filsys*)unixfs->s_fs_info)->s_fsize) { 316 fprintf(stderr, 317 "***fatal error: bread failed for block %llu\n", blkno); 318 abort(); 319 /* NOTREACHED */ 320 } 321 322 if (pread(unixfs->s_bdev, blkbuf, UNIXFS_IOSIZE(unixfs), 323 blkno * (off_t)BSIZE) != UNIXFS_IOSIZE(unixfs)) 324 return EIO; 325 326 return 0; 327} 328 329static struct inode* 330unixfs_internal_iget(ino_t ino) 331{ 332 struct inode* ip = unixfs_inodelayer_iget(ino); 333 if (!ip) { 334 fprintf(stderr, "*** fatal error: no inode for %llu\n", (ino64_t)ino); 335 abort(); 336 } 337 338 if (ip->I_initialized) 339 return ip; 340 341 unixfs_inodelayer_ifailed(ip); 342 343 return NULL; 344} 345 346static void 347unixfs_internal_iput(struct inode* ip) 348{ 349 unixfs_inodelayer_iput(ip); 350} 351 352static int 353unixfs_internal_igetattr(ino_t ino, struct stat* stbuf) 354{ 355 struct inode* ip = unixfs_internal_iget(ino); 356 if (!ip) 357 return ENOENT; 358 359 unixfs_internal_istat(ip, stbuf); 360 361 unixfs_internal_iput(ip); 362 363 return 0; 364} 365 366static void 367unixfs_internal_istat(struct inode* ip, struct stat* stbuf) 368{ 369 memcpy(stbuf, &ip->I_stat, sizeof(struct stat)); 370 stbuf->st_mode = ancientfs_dtp_mode(ip->I_mode, unixfs->s_flags); 371} 372 373static int 374unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf) 375{ 376 int ret = ENOENT; 377 stbuf->st_ino = 0; 378 379 size_t namelen = strlen(name); 380 if (namelen > DIRSIZ) 381 return ENAMETOOLONG; 382 383 struct inode* dp = unixfs_internal_iget(parentino); 384 if (!dp) 385 return ENOENT; 386 387 if (!S_ISDIR(ancientfs_dtp_mode(dp->I_mode, unixfs->s_flags))) { 388 ret = ENOTDIR; 389 goto out; 390 } 391 392 struct tap_node_info* child = 393 ((struct tap_node_info*)dp->I_private)->ti_children;; 394 395 if (!child) { 396 ret = ENOENT; 397 goto out; 398 } 399 400 int found = 0; 401 402 do { 403 size_t target_namelen = strlen((const char*)child->ti_name); 404 if ((namelen == target_namelen) && 405 (memcmp(name, child->ti_name, target_namelen) == 0)) { 406 found = 1; 407 break; 408 } 409 child = child->ti_next_sibling; 410 } while (child); 411 412 if (found) 413 ret = unixfs_internal_igetattr((ino_t)child->ti_self->I_ino, stbuf); 414 415out: 416 unixfs_internal_iput(dp); 417 418 return ret; 419} 420 421static int 422unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf, 423 off_t* offset, struct unixfs_direntry* dent) 424{ 425 if (*offset >= dp->I_size) 426 return -1; 427 428 if (*offset < 2) { 429 int idx = 0; 430 dent->name[idx++] = '.'; 431 dent->ino = ROOTINO; 432 if (*offset == 1) { 433 if (dp->I_ino != ROOTINO) { 434 struct inode* pdp = unixfs_internal_iget(dp->I_ino); 435 if (pdp) { 436 dent->ino = pdp->I_ino; 437 unixfs_internal_iput(pdp); 438 } 439 } 440 dent->name[idx++] = '.'; 441 } 442 dent->name[idx++] = '\0'; 443 goto out; 444 } 445 446 struct tap_node_info* child = 447 ((struct tap_node_info*)dp->I_private)->ti_children;; 448 449 off_t i; 450 451 for (i = 0; i < (*offset - 2); i++) 452 child = child->ti_next_sibling; 453 454 dent->ino = (ino_t)child->ti_self->I_ino; 455 size_t dirnamelen = min(DIRSIZ, UNIXFS_MAXNAMLEN); 456 memcpy(dent->name, child->ti_name, dirnamelen); 457 dent->name[dirnamelen] = '\0'; 458 459out: 460 *offset += 1; 461 462 return 0; 463} 464 465static ssize_t 466unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset, 467 int* error) 468{ 469 ssize_t done = 0; 470 size_t tomove = 0; 471 ssize_t remaining = nbyte; 472 ssize_t iosize = UNIXFS_IOSIZE(unixfs); 473 char blkbuf[iosize]; 474 char* p = buf; 475 476 while (remaining > 0) { 477 off_t lbn = offset / BSIZE; 478 off_t bn = unixfs_internal_bmap(ip, lbn, error); 479 if (UNIXFS_BADBLOCK(bn, *error)) 480 break; 481 *error = unixfs_internal_bread(bn, blkbuf); 482 if (*error != 0) 483 break; 484 tomove = (remaining > iosize) ? iosize : remaining; 485 memcpy(p, blkbuf, tomove); 486 remaining -= tomove; 487 done += tomove; 488 offset += tomove; 489 p += tomove; 490 } 491 492 if ((done == 0) && *error) 493 return -1; 494 495 return done; 496} 497 498static int 499unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN]) 500{ 501 return ENOSYS; 502} 503 504static int 505unixfs_internal_sanitycheck(void* filsys, off_t disksize) 506{ 507 return 0; 508} 509 510static int 511unixfs_internal_statvfs(struct statvfs* svb) 512{ 513 memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs)); 514 return 0; 515}