PageRenderTime 55ms CodeModel.GetById 2ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/ancientfs/ancientfs_dumpvn.c

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