PageRenderTime 83ms CodeModel.GetById 42ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/ancientfs/ancientfs_2.11bsd.c

http://macfuse.googlecode.com/
C | 664 lines | 509 code | 130 blank | 25 comment | 122 complexity | e0e2eb9945c4406ba59eede63b2054a2 MD5 | raw file
  1/*
  2 * Ancient UNIX File Systems for MacFUSE
  3 * Amit Singh
  4 * http://osxbook.com
  5 */
  6
  7#include "ancientfs_2.11bsd.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("2.11BSD", 211bsd);
 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;
 32    struct stat stbuf;
 33    struct super_block* sb = (struct super_block*)0;
 34    struct fs* fs = (struct fs*)0;
 35
 36    if ((err = fstat(fd, &stbuf)) != 0) {
 37        perror("fstat");
 38        goto out;
 39    }
 40
 41    if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) {
 42        err = EINVAL;
 43        fprintf(stderr, "%s is not a disk image file\n", dmg);
 44        goto out;
 45    }
 46
 47    sb = malloc(sizeof(struct super_block));
 48    if (!sb) {
 49        err = ENOMEM;
 50        goto out;
 51    }
 52
 53    assert(sizeof(struct fs) <= DEV_BSIZE);
 54
 55    fs = malloc(DEV_BSIZE);
 56    if (!fs) {
 57        free(sb);
 58        err = ENOMEM;
 59        goto out;
 60    }
 61
 62    if (pread(fd, fs, SBSIZE, (off_t)(DEV_BSIZE * SUPERB)) != SBSIZE) {
 63        perror("pread");
 64        err = EIO;
 65        goto out;
 66    }
 67
 68    unixfs = sb;
 69
 70    unixfs->s_flags = flags;
 71    unixfs->s_endian = (fse == UNIXFS_FS_INVALID) ? UNIXFS_FS_PDP : fse;
 72    unixfs->s_fs_info = (void*)fs;
 73    unixfs->s_bdev = fd;
 74
 75    fs->s_isize = fs16_to_host(unixfs->s_endian, fs->s_isize);
 76    fs->s_fsize = fs32_to_host(unixfs->s_endian, fs->s_fsize);
 77    fs->s_nfree = fs16_to_host(unixfs->s_endian, fs->s_nfree);
 78
 79    for (i = 0; i < NICFREE; i++)
 80        fs->s_free[i] = fs32_to_host(unixfs->s_endian, fs->s_free[i]);
 81    fs->s_ninode = fs16_to_host(unixfs->s_endian, fs->s_ninode);
 82    for (i = 0; i < NICINOD; i++)
 83        fs->s_inode[i] = fs16_to_host(unixfs->s_endian, fs->s_inode[i]);
 84    fs->s_time = fs32_to_host(unixfs->s_endian, fs->s_time);
 85
 86    unixfs->s_statvfs.f_bsize = DEV_BSIZE;
 87    unixfs->s_statvfs.f_frsize = DEV_BSIZE;
 88
 89    /* must initialize the inode layer before sanity checking */
 90    if ((err = unixfs_inodelayer_init(0)) != 0)
 91        goto out;
 92
 93    if (unixfs_internal_sanitycheck(fs, stbuf.st_size) != 0) {
 94        if (!(flags & UNIXFS_FORCE)) {
 95            fprintf(stderr,
 96              "retry with the --force option to see if it will work anyway\n");
 97            err = EINVAL;
 98            goto out;
 99        }
100    }
101
102    int iblock;
103    unixfs->s_statvfs.f_files = 0;
104    unixfs->s_statvfs.f_ffree = 0;
105
106    char* ubuf = malloc(UNIXFS_IOSIZE(unixfs));
107    if (!ubuf) {
108        err = ENOMEM;
109        goto out;
110    }
111
112    for (iblock = 2; iblock < fs->s_isize; iblock++) {
113        if (unixfs_internal_bread((off_t)iblock, ubuf) != 0)
114            continue;
115        struct dinode* dip = (struct dinode*)ubuf;
116        for (i = 0; i < INOPB; i++, dip++) {
117            if (fs16_to_host(unixfs->s_endian, dip->di_nlink) == 0)
118                unixfs->s_statvfs.f_ffree++;
119            else
120                unixfs->s_statvfs.f_files++;
121        }
122    }
123
124    free(ubuf);
125
126    unixfs->s_statvfs.f_blocks = fs->s_fsize;
127    unixfs->s_statvfs.f_bfree = 0;
128
129    while (unixfs_internal_alloc())
130        unixfs->s_statvfs.f_bfree++;
131
132    unixfs->s_statvfs.f_bavail = unixfs->s_statvfs.f_bfree;
133    unixfs->s_dentsize = 0; /* no fixed size */
134    unixfs->s_statvfs.f_namemax = MAXNAMLEN;
135
136    snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "%s", unixfs_fstype);
137
138    char* dmg_basename = basename((char*)dmg);
139    snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (disk=%s)",
140             unixfs_fstype, (dmg_basename) ? dmg_basename : "Disk Image");
141
142    *fsname = unixfs->s_fsname;
143    *volname = unixfs->s_volname;
144
145out:
146    if (err) {
147        if (fd >= 0)
148            close(fd);
149        if (fs)
150            free(fs);
151        if (sb)
152            free(sb);
153        return NULL;
154    }
155
156    return sb;
157}
158
159static void
160unixfs_internal_fini(void* filsys)
161{
162    unixfs_inodelayer_fini();
163    struct super_block* sb = (struct super_block*)filsys;
164    if (sb) {
165        if (sb->s_bdev >= 0)
166            close(sb->s_bdev);
167        sb->s_bdev = -1;
168        if (sb->s_fs_info)
169            free(sb->s_fs_info);
170    }
171}
172
173static off_t
174unixfs_internal_alloc(void)
175{
176    struct fs* fs = (struct fs*)unixfs->s_fs_info;
177
178    a_int i = --fs->s_nfree;
179    if (i < 0)
180        goto nospace;
181
182    if (i > NICFREE) /* bad free count */
183        return (off_t)0;
184
185    a_daddr_t bno = fs->s_free[i];
186    if (bno == 0)
187        return (off_t)0;
188
189    if (bno < fs->s_isize || bno >= fs->s_fsize)
190        return (off_t)0; /* bad free block <bno> */
191
192    if (fs->s_nfree <= 0) {
193        char ubuf[UNIXFS_IOSIZE(unixfs)];
194        int ret = unixfs_internal_bread((off_t)bno, ubuf);
195        if (ret == 0) {
196            struct fblk* fblk = (struct fblk*)ubuf;
197            fs->s_nfree = fs16_to_host(unixfs->s_endian, fblk->df_nfree);
198            for (i = 0; i < NICFREE; i++)
199                fs->s_free[i] = fs32_to_host(unixfs->s_endian, fblk->df_free[i]);
200        } else
201            return (off_t)0;
202    }
203
204    return (off_t)bno;
205
206nospace:
207    fs->s_nfree = 0;
208    return (off_t)0; /* ENOSPC */
209}
210
211static off_t
212unixfs_internal_bmap(struct inode* ip, off_t lblkno, int* error)
213{
214    a_daddr_t bn = (a_daddr_t)lblkno;
215
216    if (bn < 0) {
217        *error = EFBIG;
218        return (off_t)0;
219    }
220
221    *error = EROFS;
222
223    /*
224     * blocks 0..NADDR-4 are direct blocks
225     */
226
227    int i;
228    a_daddr_t nb;
229
230    if (bn < (NADDR - 3)) {
231        i = bn;
232        nb = ip->I_daddr[i];
233        if (nb == 0)
234            return (off_t)0; /* !writable; should be -1 rather */
235        *error = 0;
236        return (off_t)nb;
237    }
238
239    /*
240     * addresses NADDR - 3, NADDR - 2, and NADDR - 1 have single, double,
241     * and triple indirect blocks. the first step is to determine how many
242     * levels of indirection.
243     */
244
245    int j, sh = 0;
246    nb = 1;
247    bn -= NADDR - 3;
248    for (j = 3; j > 0; j--) { 
249        sh += NSHIFT;
250        nb <<= NSHIFT;
251        if (bn < nb)
252            break;
253        bn -= nb;
254    }
255    if (j == 0) {
256        *error = EFBIG;
257        return (off_t)0;
258    }
259
260    /*
261     * fetch the first indirect block
262     */
263
264    nb = ip->I_daddr[NADDR - j];
265    if (nb == 0)
266        return (off_t)0; /* !writable; should be -1 rather */
267
268    /*
269     * fetch through the indirect blocks
270     */
271
272    for (; j <= 3; j++) {
273        char ubuf[UNIXFS_IOSIZE(unixfs)];
274        int ret = unixfs_internal_bread((off_t)nb, ubuf);
275        if (ret) {
276            *error = ret;
277            return (off_t)0;
278        }
279        a_daddr_t* bap = (a_daddr_t*)ubuf;
280        sh -= NSHIFT;
281        i = (bn >> sh) & NMASK;
282        nb = fs32_to_host(unixfs->s_endian, bap[i]);
283        if (nb == 0)
284            return (off_t)0; /* !writable; should be -1 rather */
285    }
286
287    /* calculate read-ahead here. */
288
289    *error = 0;
290
291    return (off_t)nb;
292}
293
294static int
295unixfs_internal_bread(off_t blkno, char* blkbuf)
296{
297    if (blkno >= ((struct fs*)unixfs->s_fs_info)->s_fsize) {
298        fprintf(stderr,
299                "***fatal error: bread failed for block %llu\n", blkno);
300        abort();
301        /* NOTREACHED */
302    }
303
304    if (blkno == 0) { /* zero fill */
305        memset(blkbuf, 0, UNIXFS_IOSIZE(unixfs));
306        return 0;
307    }
308
309    if (pread(unixfs->s_bdev, blkbuf, UNIXFS_IOSIZE(unixfs),
310              blkno * (off_t)DEV_BSIZE) != UNIXFS_IOSIZE(unixfs))
311        return EIO;
312
313    return 0;
314}
315
316static struct inode*
317unixfs_internal_iget(ino_t ino)
318{
319    if (ino == MACFUSE_ROOTINO)
320        ino = ROOTINO;
321
322    struct inode* ip = unixfs_inodelayer_iget(ino);
323    if (!ip) {
324        fprintf(stderr, "*** fatal error: no inode for %llu\n", (ino64_t)ino);
325        abort();
326    }
327
328    if (ip->I_initialized)
329        return ip;
330
331    char ubuf[UNIXFS_IOSIZE(unixfs)];
332
333    if (unixfs_internal_bread((off_t)itod((a_ino_t)ino), ubuf) != 0) {
334        unixfs_inodelayer_ifailed(ip);
335        return NULL;
336    }
337
338    struct dinode* dip = (struct dinode*)ubuf;
339    dip += itoo((a_ino_t)ino);
340
341    ip->I_number = ino;
342
343    /* ip->I_ic1 = dip->di_ic1 */
344
345    ip->I_mode  = fs16_to_host(unixfs->s_endian, dip->di_mode);
346    ip->I_nlink = fs16_to_host(unixfs->s_endian, dip->di_nlink);
347    ip->I_uid   = fs16_to_host(unixfs->s_endian, dip->di_uid);
348    ip->I_gid   = fs16_to_host(unixfs->s_endian, dip->di_gid);
349    ip->I_size  = fs32_to_host(unixfs->s_endian, dip->di_size);
350
351#ifndef EXTERNALTIMES
352
353    /* ip->I_ic2 = dip->di_ic2 */
354
355    ip->I_atime_sec = fs32_to_host(unixfs->s_endian, dip->di_atime);
356    ip->I_mtime_sec = fs32_to_host(unixfs->s_endian, dip->di_mtime);
357    ip->I_ctime_sec = fs32_to_host(unixfs->s_endian, dip->di_ctime);
358
359#endif
360
361    int i;
362
363    for (i = 0; i < NADDR; i++)
364        ip->I_daddr[i] = fs32_to_host(unixfs->s_endian, dip->di_addr[i]);
365
366    if (S_ISCHR(ip->I_mode) || S_ISBLK(ip->I_mode)) {
367        uint32_t rdev = ip->I_daddr[0];
368        ip->I_rdev = makedev((rdev >> 8) & 255, rdev & 255);
369    }
370
371    unixfs_inodelayer_isucceeded(ip);
372
373    return ip;
374}
375
376static void
377unixfs_internal_iput(struct inode* ip)
378{
379    unixfs_inodelayer_iput(ip);
380}
381
382static int
383unixfs_internal_igetattr(ino_t ino, struct stat* stbuf)
384{
385    if (ino == MACFUSE_ROOTINO)
386        ino = ROOTINO;
387
388    struct inode* ip = unixfs_internal_iget(ino);
389    if (!ip)
390        return ENOENT;
391
392    unixfs_internal_istat(ip, stbuf);
393
394    unixfs_internal_iput(ip);
395
396    return 0;
397}
398
399static void
400unixfs_internal_istat(struct inode* ip, struct stat* stbuf)
401{
402    memcpy(stbuf, &ip->I_stat, sizeof(struct stat));
403}
404
405#define	roundup(x, y) ((((x)+((y)-1))/(y))*(y))
406
407static int
408__unixfs_internal_blkatoff(struct inode* ip, off_t offset, char* ubuf)
409{
410    int error;
411
412    off_t lbn = lblkno(offset);
413    off_t bn = unixfs_internal_bmap(ip, lbn, &error);
414    if (bn == 0)
415        return error;
416
417    return unixfs_internal_bread(bn, ubuf);
418}
419
420static int
421__unixfs_internal_dirbadentry(struct direct* ep, int entryoffsetinblock)
422{
423    int i;
424
425    if ((ep->d_reclen & 0x3) != 0 ||
426         ep->d_reclen > ANCIENTFS_211BSD_DIRBLKSIZ - \
427             (entryoffsetinblock & (ANCIENTFS_211BSD_DIRBLKSIZ - 1)) ||
428         ep->d_reclen < ANCIENTFS_211BSD_DIRSIZ(ep) ||
429         ep->d_namlen > UNIXFS_MAXNAMLEN)
430        return 1;
431
432    for (i = 0; i < ep->d_namlen; i++)
433        if (ep->d_name[i] == '\0')
434            return 1;
435
436    return ep->d_name[i];
437}
438
439static int
440unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf)
441{
442    if (parentino == MACFUSE_ROOTINO)
443        parentino = ROOTINO;
444
445    stbuf->st_ino = 0;
446
447    struct inode* dp = unixfs_internal_iget(parentino);
448    if (!dp)
449        return ENOENT;
450
451    if (!S_ISDIR(dp->I_mode)) {
452        unixfs_internal_iput(dp);
453        return ENOTDIR;
454    }
455
456    int ret = ENOENT, i, found = 0;
457    off_t ni_offset = 0, entryoffsetinblock = 0;
458    int endsearch = roundup(dp->I_size, ANCIENTFS_211BSD_DIRBLKSIZ);
459
460    struct direct* ep;
461    char ubuf[UNIXFS_IOSIZE(unixfs)];
462
463    size_t namlen = strlen(name);
464
465    while (ni_offset < endsearch) {
466
467        if (blkoff(ni_offset) == 0) {
468            ret = __unixfs_internal_blkatoff(dp, ni_offset, ubuf);
469            if (ret) {
470                ret = ENOENT;
471                goto out;
472            }
473            entryoffsetinblock = 0;
474        }
475        ep = (struct direct*)((char*)ubuf + entryoffsetinblock);
476        ep->d_ino = fs16_to_host(unixfs->s_endian, ep->d_ino);
477        ep->d_reclen = fs16_to_host(unixfs->s_endian, ep->d_reclen);
478        ep->d_namlen = fs16_to_host(unixfs->s_endian, ep->d_namlen);
479        if (ep->d_reclen == 0 ||
480            __unixfs_internal_dirbadentry(ep, entryoffsetinblock)) {
481            i = ANCIENTFS_211BSD_DIRBLKSIZ -
482                    (entryoffsetinblock & (ANCIENTFS_211BSD_DIRBLKSIZ - 1));
483            ni_offset += i;
484            entryoffsetinblock += i;
485            continue;
486        }
487        if (ep->d_ino) {
488            if ((namlen == ep->d_namlen) &&
489                bcmp(name, ep->d_name, ep->d_namlen) == 0) {
490                found = 1;
491                break;
492            }
493        }
494        ni_offset += ep->d_reclen;
495        entryoffsetinblock += ep->d_reclen;
496
497    } /* while */
498
499out:
500    unixfs_internal_iput(dp);
501
502    if (found) /* matched */
503        ret = unixfs_internal_igetattr((ino_t)(ep->d_ino), stbuf);
504
505    return ret;
506}
507
508static int
509unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf,
510                             off_t* offset, struct unixfs_direntry* dent)
511{
512    struct direct* ep;
513    off_t ni_offset = *offset;
514    off_t entryoffsetinblock = blkoff(ni_offset);
515    int endsearch = roundup(dp->I_size, ANCIENTFS_211BSD_DIRBLKSIZ);
516
517    if (ni_offset >= endsearch)
518        return -1;
519
520    if (!dirbuf->flags.initialized || (blkoff(ni_offset) == 0)) {
521        int ret = __unixfs_internal_blkatoff(dp, ni_offset, dirbuf->data);
522        if (ret)
523            return ret;
524        entryoffsetinblock = 0;
525        dirbuf->flags.initialized = 1;
526    }
527    ep = (struct direct*)((char*)dirbuf->data + entryoffsetinblock);
528    ep->d_ino = fs16_to_host(unixfs->s_endian, ep->d_ino);
529    ep->d_reclen = fs16_to_host(unixfs->s_endian, ep->d_reclen);
530    ep->d_namlen = fs16_to_host(unixfs->s_endian, ep->d_namlen);
531    if (ep->d_reclen == 0 ||
532        __unixfs_internal_dirbadentry(ep, entryoffsetinblock)) {
533        int i =
534            ANCIENTFS_211BSD_DIRBLKSIZ - (entryoffsetinblock &
535                (ANCIENTFS_211BSD_DIRBLKSIZ - 1));
536        ni_offset += i;
537        entryoffsetinblock += i;
538        dent->ino = 0;
539    } else {
540        dent->ino = (ino_t)ep->d_ino;
541        memcpy(dent->name, ep->d_name, ep->d_namlen);
542        dent->name[ep->d_namlen] = '\0';
543        ni_offset += ep->d_reclen;
544        entryoffsetinblock += ep->d_reclen;
545    }
546
547    *offset = ni_offset;
548
549    return 0;
550}
551
552static ssize_t
553unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset,
554                       int* error)
555{
556    ssize_t done = 0;
557    size_t tomove = 0;
558    ssize_t remaining = nbyte;
559    ssize_t iosize = UNIXFS_IOSIZE(unixfs);
560    char blkbuf[iosize];
561    char* p = buf;
562
563    while (remaining > 0) {
564        off_t lbn = offset / DEV_BSIZE;
565        off_t bn = unixfs_internal_bmap(ip, lbn, error);
566        if (UNIXFS_BADBLOCK(bn, *error))
567            break;
568        *error = unixfs_internal_bread(bn, blkbuf);
569        if (*error != 0)
570            break;
571        tomove = (remaining > iosize) ? iosize : remaining;
572        memcpy(p, blkbuf, tomove);
573        remaining -= tomove;
574        done += tomove;
575        offset += tomove;
576        p += tomove;
577    }
578
579    if ((done == 0) && *error)
580        return -1;
581
582    return done;
583}
584
585static int
586unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN])
587{
588    struct inode* ip = unixfs_internal_iget(ino);
589    if (!ip)
590        return ENOENT;
591
592    int error;
593
594    off_t bn = unixfs_internal_bmap(ip, (off_t)0, &error);
595    if (UNIXFS_BADBLOCK(bn, error))
596        goto out;
597
598    /* we know MAXPATHLEN (256) < DEV_BSIZE (1024) == UNIXFS_MAXPATHLEN */
599    error = unixfs_internal_bread(bn, path);
600    if (error)
601        goto out;
602
603    size_t linklen = (ip->I_size > DEV_BSIZE - 1) ? DEV_BSIZE - 1: ip->I_size;
604    path[linklen] = '\0';
605    error = 0;
606
607out:
608    unixfs_internal_iput(ip);
609
610    return error;
611}
612
613static int
614unixfs_internal_sanitycheck(void* filsys, off_t disksize)
615{
616    struct fs* fs = (struct fs*)filsys;
617
618    if ((off_t)(fs->s_fsize * DEV_BSIZE) > disksize) {
619        fprintf(stderr, "*** disk image seems smaller than the volume\n");
620        return -1;
621    }
622
623    if (fs->s_nfree > NICFREE) {
624        fprintf(stderr, "*** warning: implausible s_nfree %hu\n", fs->s_nfree);
625        return -1;
626    }
627
628    if (fs->s_ninode > NICINOD) {
629        fprintf(stderr,
630                "*** warning: implausible s_ninode %hu\n", fs->s_ninode);
631        return -1;
632    }
633
634    if (fs->s_time == 0) {
635        fprintf(stderr, "*** warning: implausible timestamp of 0\n");
636        return -1;
637    }
638
639    struct stat stbuf;
640    int ret = unixfs_internal_igetattr((ino_t)ROOTINO, &stbuf);
641    if (ret) {
642        fprintf(stderr, "*** warning: failed to get root inode\n");
643        return -1;
644    }
645
646    if (!S_ISDIR(stbuf.st_mode)) {
647        fprintf(stderr, "*** warning: root inode is not a directory\n");
648        return -1;
649    }
650
651    if (stbuf.st_size == 0) {
652        fprintf(stderr, "*** warning: root inode has zero size\n");
653        return -1;
654    }
655
656    return 0;
657}
658
659static int
660unixfs_internal_statvfs(struct statvfs* svb)
661{
662    memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs));
663    return 0;
664}