PageRenderTime 137ms CodeModel.GetById 76ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/ancientfs/ancientfs_dtp.c

http://macfuse.googlecode.com/
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}