static struct filesystem_entry *find_filesystem_entry( struct filesystem_entry *dir, char *fullname, uint32_t type) { struct filesystem_entry *e = dir; if (S_ISDIR(dir->sb.st_mode)) { e = dir->files; } while (e) { /* Only bother to do the expensive strcmp on matching file types */ if (type == (e->sb.st_mode & S_IFMT)) { if (S_ISDIR(e->sb.st_mode)) { int len = strlen(e->fullname); /* Check if we are a parent of the correct path */ if (strncmp(e->fullname, fullname, len) == 0) { /* Is this an _exact_ match? */ if (strcmp(fullname, e->fullname) == 0) { return (e); } /* Looks like we found a parent of the correct path */ if (fullname[len] == '/') { if (e->files) { return (find_filesystem_entry (e, fullname, type)); } else { return NULL; } } } } else { if (strcmp(fullname, e->fullname) == 0) { return (e); } } } e = e->next; } return (NULL); }
/* device table entries take the form of: <path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count> /dev/mem c 640 0 0 1 1 0 0 - type can be one of: f A regular file d Directory c Character special device file b Block special device file p Fifo (named pipe) I don't bother with symlinks (permissions are irrelevant), hard links (special cases of regular files), or sockets (why bother). Regular files must exist in the target root directory. If a char, block, fifo, or directory does not exist, it will be created. */ static int interpret_table_entry(struct filesystem_entry *root, char *line) { char *hostpath; char type, *name = NULL, *tmp, *dir; unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; unsigned long start = 0, increment = 1, count = 0; struct filesystem_entry *parent, *entry; if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu", SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor, &start, &increment, &count) < 0) { return 1; } if (!strcmp(name, "/")) { error_msg_and_die("Device table entries require absolute paths"); } asprintf(&hostpath, "%s%s", rootdir, name); /* Check if this file already exists... */ switch (type) { case 'd': mode |= S_IFDIR; break; case 'f': mode |= S_IFREG; break; case 'p': mode |= S_IFIFO; break; case 'c': mode |= S_IFCHR; break; case 'b': mode |= S_IFBLK; break; default: error_msg_and_die("Unsupported file type"); } entry = find_filesystem_entry(root, name, mode); if (entry) { /* Ok, we just need to fixup the existing entry * and we will be all done... */ entry->sb.st_uid = uid; entry->sb.st_gid = gid; entry->sb.st_mode = mode; if (major && minor) { entry->sb.st_rdev = makedev(major, minor); } } else { /* If parent is NULL (happens with device table entries), * try and find our parent now) */ tmp = strdup(name); dir = dirname(tmp); parent = find_filesystem_entry(root, dir, S_IFDIR); free(tmp); if (parent == NULL) { error_msg ("skipping device_table entry '%s': no parent directory!", name); free(name); free(hostpath); return 1; } switch (type) { case 'd': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'f': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'p': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'c': case 'b': if (count > 0) { dev_t rdev; unsigned long i; char *dname, *hpath; for (i = start; i < count; i++) { asprintf(&dname, "%s%lu", name, i); asprintf(&hpath, "%s/%s%lu", rootdir, name, i); rdev = makedev(major, minor + (i * increment - start)); add_host_filesystem_entry(dname, hpath, uid, gid, mode, rdev, parent); free(dname); free(hpath); } } else { dev_t rdev = makedev(major, minor); add_host_filesystem_entry(name, hostpath, uid, gid, mode, rdev, parent); } break; default: error_msg_and_die("Unsupported file type"); } } free(name); free(hostpath); return 0; }
void modify_entry(char *full_path, unsigned long uid, unsigned long gid, unsigned long mode, unsigned long rdev, struct entry *root, off_t *fslen_ub) { char *name, *path, *full; struct entry *curr, *parent, *entry, *prev; full = xstrdup(full_path); path = xstrdup(dirname(full)); name = full_path + strlen(path) + 1; free(full); if (strcmp(path, "/") == 0) { parent = root; name = full_path + 1; } else { if (!(parent = find_filesystem_entry(root, path+1, S_IFDIR))) error_msg_and_die("%s/%s: could not find parent\n", path, name); } if ((entry = find_filesystem_entry(parent, name, (mode & S_IFMT)))) { /* its there, just modify permissions */ entry->mode = mode; entry->uid = uid; entry->gid = gid; } else { /* make a new entry */ /* code partially replicated from parse_directory() */ size_t namelen; if (S_ISREG(mode)) { error_msg_and_die("%s: regular file from device_table file must exist on disk!", full_path); } namelen = strlen(name); if (namelen > MAX_INPUT_NAMELEN) { error_msg_and_die( "Very long (%u bytes) filename `%s' found.\n" " Please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile. Exiting.\n", namelen, name); } entry = xcalloc(1, sizeof(struct entry)); entry->name = xstrdup(name); /* truncate multi-byte UTF-8 filenames on character boundary */ if (namelen > CRAMFS_MAXPATHLEN) { namelen = CRAMFS_MAXPATHLEN; warn_namelen = 1; /* the first lost byte must not be a trail byte */ while ((entry->name[namelen] & 0xc0) == 0x80) { namelen--; /* are we reasonably certain it was UTF-8 ? */ if (entry->name[namelen] < 0x80 || !namelen) { error_msg_and_die("cannot truncate filenames not encoded in UTF-8"); } } entry->name[namelen] = '\0'; } entry->mode = mode; entry->uid = uid; entry->gid = gid; entry->size = 0; if (S_ISBLK(mode) || S_ISCHR(mode)) { entry->size = rdev; if (entry->size & -(1<<CRAMFS_SIZE_WIDTH)) warn_dev = 1; } /* ok, now we have to backup and correct the size of all the entries above us */ *fslen_ub += sizeof(struct cramfs_inode) + ((namelen + 3) & ~3); parent->size += sizeof(struct cramfs_inode) + ((namelen + 3) & ~3); /* alright, time to link us in */ curr = parent->child; prev = NULL; while (curr && strcmp(name, curr->name) > 0) { prev = curr; curr = curr->next; } if (!prev) parent->child = entry; else prev->next = entry; entry->next = curr; entry->child = NULL; } if (entry->uid >= 1 << CRAMFS_UID_WIDTH) warn_uid = 1; if (entry->gid >= 1 << CRAMFS_GID_WIDTH) { /* TODO: We ought to replace with a default gid instead of truncating; otherwise there are security problems. Maybe mode should be &= ~070. Same goes for uid once Linux supports >16-bit uids. */ warn_gid = 1; } free(path); }