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; }