示例#1
0
static void
unixfs_internal_iput(struct inode* ip)
{
    unixfs_inodelayer_iput(ip);
}
static void*
unixfs_internal_init(const char* dmg, uint32_t flags, fs_endian_t fse,
                     char** fsname, char** volname)
{
    int fd = -1;
    if ((fd = open(dmg, O_RDONLY)) < 0) {
        perror("open");
        return NULL;
    }

    int err;
    struct stat stbuf;
    struct super_block* sb = (struct super_block*)0;
    struct filsys* fs = (struct filsys*)0;

    if ((err = fstat(fd, &stbuf)) != 0) {
        perror("fstat");
        goto out;
    }

    if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) {
        err = EINVAL;
        fprintf(stderr, "%s is not a tape image file\n", dmg);
        goto out;
    }

    struct cpio_newc_header hdr;

    if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
        fprintf(stderr, "failed to read data from file\n");
        err = EIO;
        goto out;
    }

    char* magic = CPIO_NEWC_MAGIC;
    if (unixfs->s_flags & ANCIENTFS_NEWCRC)
        magic = CPIO_NEWCRC_MAGIC;

    if (strncmp(hdr.c_magic, magic, CPIO_NEWC_MAGLEN) != 0) {
        fprintf(stderr, "not recognized as a cpio_newc archive\n");
        err = EINVAL;
        goto out;
    }

    sb = malloc(sizeof(struct super_block));
    if (!sb) {
        err = ENOMEM;
        goto out;
    }

    assert(sizeof(struct filsys) <= CPIO_NEWC_BLOCK);

    fs = calloc(1, CPIO_NEWC_BLOCK);
    if (!fs) {
        free(sb);
        err = ENOMEM;
        goto out;
    }

    unixfs = sb;

    unixfs->s_flags = flags;

    /* not used */
    unixfs->s_endian = (fse == UNIXFS_FS_INVALID) ? UNIXFS_FS_LITTLE : fse;

    unixfs->s_fs_info = (void*)fs;
    unixfs->s_bdev = fd;

    /* must initialize the inode layer before sanity checking */
    if ((err = unixfs_inodelayer_init(sizeof(struct cpio_newc_node_info))) != 0)
        goto out;

    struct inode* rootip = unixfs_inodelayer_iget((ino_t)ROOTINO);
    if (!rootip) {
        fprintf(stderr, "*** fatal error: no root inode\n");
        abort();
    }

    rootip->I_mode = S_IFDIR | 0755;
    rootip->I_uid  = getuid();
    rootip->I_gid  = getgid();
    rootip->I_size = 2;
    rootip->I_atime_sec = rootip->I_mtime_sec = rootip->I_ctime_sec =        time(0);

    struct cpio_newc_node_info* rootci =
        (struct cpio_newc_node_info*)rootip->I_private;
    rootci->ci_self = rootip;
    rootci->ci_parent = NULL;
    rootci->ci_children = NULL;
    rootci->ci_next_sibling = NULL;

    unixfs_inodelayer_isucceeded(rootip);

    fs->s_fsize = stbuf.st_size / CPIO_NEWC_BLOCK;
    fs->s_files = 0;
    fs->s_directories = 1 + 1 + 1;
    fs->s_rootip = rootip;
    fs->s_lastino = ROOTINO;

    lseek(fd, (off_t)0, SEEK_SET); /* rewind tape */

    struct cpio_newc_entry _ce, *ce = &_ce;

    for (;;) {
        if ((err = ancientfs_cpio_newc_readheader(fd, ce)) != 0) {
            if (err == 1)
                break;
            else {
                fprintf(stderr,
                        "*** fatal error: cannot read block (error %d)\n", err);
                err = EIO;
                goto out;
            }
        }

        char* path = ce->name;
        ino_t parent_ino = ROOTINO;
        size_t pathlen = strlen(ce->name);

        if ((*path == '.') && ((pathlen == 1) ||
            ((pathlen == 2) && (*(path + 1) == '/')))) {
            /* root */
            rootip->I_mode = ce->stat.st_mode;
            rootip->I_atime_sec = \
                rootip->I_mtime_sec = \
                    rootip->I_ctime_sec = ce->stat.st_mtime;
            continue;
        }
                
        /* we don't deal with many fancy paths here: just '/' and './' */
        if (*path == '/')
            path++;
        else if (*path == '.' && *(path + 1) == '/')
            path += 2;

        char *cnp, *term;

        for (cnp = strtok_r(path, "/", &term); cnp;
            cnp = strtok_r(NULL, "/", &term)) {
            /* we have { parent_ino, cnp } */
            struct stat stbuf;
            int missing = unixfs_internal_namei(parent_ino, cnp, &stbuf);
            if (!missing) {
                parent_ino = stbuf.st_ino;
                if (!term) { /* out of order */
                    struct inode* dirp = unixfs_inodelayer_iget(parent_ino);
                    if (!dirp || !dirp->I_initialized) {
                        fprintf(stderr,
                                "*** fatal error: inode %llu inconsistent\n",
                                (ino64_t)parent_ino);
                        abort();
                    }
                    dirp->I_mode = ce->stat.st_mode;
                    dirp->I_uid = ce->stat.st_uid;
                    dirp->I_gid = ce->stat.st_gid;
                    unixfs_inodelayer_iput(dirp);
                }
                continue;
            }
            struct inode* ip =
                unixfs_inodelayer_iget((ino_t)(fs->s_lastino + 1));
                /* unixfs_inodelayer_iget(ce->stat.st_ino); */
            if (!ip) {
                fprintf(stderr, "*** fatal error: no inode for %llu\n",
                        (ino64_t)(fs->s_lastino + 1));
                abort();
            }

            ip->I_mode  = ce->stat.st_mode;
            ip->I_uid   = ce->stat.st_uid;
            ip->I_gid   = ce->stat.st_gid;
            ip->I_size  = ce->stat.st_size;
            ip->I_nlink = ce->stat.st_nlink;
            ip->I_rdev  = ce->stat.st_rdev;

            ip->I_atime_sec = ip->I_mtime_sec = ip->I_ctime_sec =
                ce->stat.st_mtime;

            struct cpio_newc_node_info* ci =
                (struct cpio_newc_node_info*)ip->I_private;

            size_t namelen = strlen(cnp);
            ci->ci_name = malloc(namelen + 1);
            if (!ci->ci_name) {
                fprintf(stderr, "*** fatal error: cannot allocate memory\n");
                abort();
            }
            memcpy(ci->ci_name, cnp, namelen);
            ci->ci_name[namelen] = '\0';

            ip->I_daddr[0] = 0;

            if (S_ISLNK(ip->I_mode)) {
                namelen = strlen(ce->linktargetname);
                ci->ci_linktargetname = malloc(namelen + 1);
                if (!ci->ci_name) {
                    fprintf(stderr,
                            "*** fatal error: cannot allocate memory\n");
                    abort();
                }
                memcpy(ci->ci_linktargetname, ce->linktargetname, namelen);
                ci->ci_linktargetname[namelen] = '\0';
            } else if (S_ISREG(ip->I_mode)) {

                ip->I_daddr[0] = ce->daddr;
            }
             
            ci->ci_self = ip;
            ci->ci_children = NULL;
            struct inode* parent_ip = unixfs_internal_iget(parent_ino);
            parent_ip->I_size += 1;
            ci->ci_parent = (struct cpio_newc_node_info*)(parent_ip->I_private);
            ci->ci_next_sibling = ci->ci_parent->ci_children;
            ci->ci_parent->ci_children = ci;

            if (term && !S_ISDIR(ip->I_mode)) /* out of order */
                ip->I_mode = S_IFDIR | 0755;

            if (S_ISDIR(ip->I_mode)) {
                fs->s_directories++;
                parent_ino = fs->s_lastino + 1;
                /* parent_ino = ip->I_ino; */
                ip->I_size = 2;
            } else
                fs->s_files++;

            fs->s_lastino++;

            /* if (ip->I_ino > fs->s_lastino)
                fs->s_lastino = ip->I_ino; */

            unixfs_internal_iput(parent_ip);
            unixfs_inodelayer_isucceeded(ip);
            /* no put */

        } /* for each component */

    } /* for each block */

    err = 0;

    unixfs->s_statvfs.f_bsize = CPIO_NEWC_BLOCK;
    unixfs->s_statvfs.f_frsize = CPIO_NEWC_BLOCK;
    unixfs->s_statvfs.f_ffree = 0;
    unixfs->s_statvfs.f_files = fs->s_files + fs->s_directories;
    unixfs->s_statvfs.f_blocks = fs->s_fsize;
    unixfs->s_statvfs.f_bfree = 0;
    unixfs->s_statvfs.f_bavail = 0;
    unixfs->s_dentsize = 1;
    unixfs->s_statvfs.f_namemax = UNIXFS_MAXNAMLEN;

    snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "ASCII cpio (newc%s)",
             (unixfs->s_flags & ANCIENTFS_NEWCRC) ? "rc" : "");

    char* dmg_basename = basename((char*)dmg);
    snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (archive=%s)",
             unixfs_fstype, (dmg_basename) ? dmg_basename : "cpio_newc Image");

    *fsname = unixfs->s_fsname;
    *volname = unixfs->s_volname;

out:
    if (err) {
        if (fd >= 0)
            close(fd);
        if (fs)
            free(fs);
        if (sb)
            free(sb);
        return NULL;
    }

    return sb;
}