PageRenderTime 49ms CodeModel.GetById 13ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/ancientfs/ancientfs_v7.c

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