--- mkcramfs-b-time.c Thu Apr 25 23:22:06 2002 +++ mkcramfs.c Fri Apr 26 00:01:22 2002 @@ -33,11 +33,14 @@ #include #include #include +#include +#include #include #include #include #include + /* Exit codes used by mkfs-type programs */ #define MKFS_OK 0 /* No errors */ #define MKFS_ERROR 8 /* Operational error */ @@ -74,6 +77,7 @@ + (1 << CRAMFS_SIZE_WIDTH) * 4 / blksize /* block pointers */ ) static const char *progname = "mkcramfs"; +static const char *meta_file_name = NULL; /* NULL means ignore meta file */ static unsigned int blksize = DEFAULT_PAGE_CACHE_SIZE; static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */ static int image_length = 0; @@ -99,6 +103,7 @@ static char *opt_name = NULL; static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid; +static int name_offset; /* For removing directory name in output */ /* In-core version of inode / directory entry. */ struct entry { @@ -133,6 +138,7 @@ " -b blocksize the page-size (default is 4096)\n" " -e edition set edition number (part of fsid) (default timestamp)\n" " -i file insert a file image into the filesystem (requires >= 2.4.0)\n" + " -m filename look for meta files with this name in each subdir\n" " -n name set name of cramfs filesystem\n" " -p pad by %d bytes for boot code\n" " -s sort directory entries (old option, ignored)\n" @@ -218,6 +224,348 @@ } } +/*****************************************************************/ + +/* Meta types */ + +#define META_IGNORE 1 +#define META_IGNORE_CONTENTS 2 +#define META_DEVICE 3 +#define META_INCLUDE 4 +#define META_UID 5 +#define META_GID 6 + +#define ID_NONE UINT_MAX /* for ignoring default_uid and default_gid */ + +struct metadata { + struct metadata *next; /* next node in linked list */ + int type; /* meta type */ + char *name; /* entry name */ + unsigned int id; /* for type META_UID and META_GID */ + char dev_type; /* for type META_DEVICE */ + unsigned char major; /* for type META_DEVICE */ + unsigned char minor; /* for type META_DEVICE */ +}; + +#define BUFFERLENGTH 256 + +/* Meta data functions */ + +struct metadata *create_meta_ignore(char *line, struct metadata *meta, int ignoretype) +{ + struct metadata *new_meta; + char *end = &line[strlen(line)]; + + while (isspace(*line)) { + line++; + } + + for (end--; end > line && isspace(*end); end--) { + *end = '\0'; + } + + if (end > line && *line == '"' && *end == '"') { + line++; + *end = '\0'; + } + + /* Can't ignore the . and .. directories */ + if (strlen(line) && strcmp(line, ".") && strcmp(line, "..")) { + if ((new_meta = malloc(sizeof(struct metadata)))) { + new_meta->next = meta; + new_meta->type = ignoretype; + new_meta->name = strdup(line); + return new_meta; + } else { + fprintf(stderr,"out of memory\n"); + exit(1); + } + } + + return meta; +} + +struct metadata *create_meta_include(char *line, struct metadata *meta) +{ + struct metadata *new_meta; + char *end = &line[strlen(line)]; + + while (isspace(*line)) { + line++; + } + + for (end--; end > line && isspace(*end); end--) { + *end = '\0'; + } + + if (end > line && *line == '"' && *end == '"') { + line++; + *end = '\0'; + } + + if ((new_meta = malloc(sizeof(struct metadata)))) { + new_meta->next = meta; + new_meta->type = META_INCLUDE; + new_meta->name = strdup(line); + return new_meta; + } else { + fprintf(stderr,"out of memory\n"); + exit(1); + } + + return meta; +} + +struct metadata *create_meta_device(char *line, struct metadata *meta) +{ + struct metadata *new_meta; + char name[MAX_INPUT_NAMELEN + 1]; + char dev_type; + unsigned int major = 0; + unsigned int minor = 0; + int found; + + found = sscanf(line, "%s %c %u %u", name, &dev_type, &major, &minor); + + if ((found == 2 || found == 4) && + (dev_type == 'b' || dev_type == 'c' || dev_type == 'p') && + major < 256 && minor < 256) { + if ((new_meta = malloc(sizeof(struct metadata)))) { + new_meta->next = meta; + new_meta->type = META_DEVICE; + new_meta->name = strdup(name); + new_meta->dev_type = dev_type; + if (dev_type == 'b' || dev_type == 'c') { + new_meta->major = (unsigned char)major; + new_meta->minor = (unsigned char)minor; + } + return new_meta; + } else { + fprintf(stderr,"out of memory\n"); + exit(1); + } + } else + fprintf(stderr, "Illegal device file specification: %s", line); + + return meta; +} + +struct metadata *create_meta_uid(char *line, struct metadata *meta) +{ + struct metadata *new_meta; + char name[MAX_INPUT_NAMELEN + 1]; + unsigned int uid; + int found; + + found = sscanf(line, "%s %u", name, &uid); + + if (found == 2) { + if ((new_meta = malloc(sizeof(struct metadata)))) { + new_meta->next = meta; + new_meta->type = META_UID; + new_meta->name = strdup(name); + new_meta->id = uid; + + return new_meta; + } else { + fprintf(stderr,"out of memory\n"); + exit(1); + } + } else + fprintf(stderr, "Illegal uid specification: %s", line); + + return meta; +} + +struct metadata *create_meta_gid(char *line, struct metadata *meta) +{ + struct metadata *new_meta; + char name[MAX_INPUT_NAMELEN + 1]; + unsigned int gid; + int found; + + found = sscanf(line, "%s %u", name, &gid); + + if (found == 2) { + if ((new_meta = malloc(sizeof(struct metadata)))) { + new_meta->next = meta; + new_meta->type = META_GID; + new_meta->name = strdup(name); + new_meta->id = gid; + + return new_meta; + } else { + fprintf(stderr,"out of memory\n"); + exit(1); + } + } else + fprintf(stderr, "Illegal gid specification: %s", line); + + return meta; +} + +struct metadata *read_meta_data(const char *dir_name, unsigned int *default_uid_ptr, unsigned int *default_gid_ptr) +{ + FILE *meta_file; + char *file_name; + struct metadata *meta = 0; + char line[BUFFERLENGTH]; + + if (!meta_file_name) + return NULL; + + if (!(file_name = malloc(strlen(dir_name) + strlen(meta_file_name) + 2))) { + fprintf(stderr,"out of memory\n"); + return 0; + } + + strcpy(file_name, dir_name); + strcat(file_name, "/"); + strcat(file_name, meta_file_name); + + if (!(meta_file = fopen(file_name, "r"))) { + return 0; + } + + while (!feof(meta_file)) { + if (fgets(line, BUFFERLENGTH - 1, meta_file)) { + if (!strncasecmp("Ignore:", line, 7)) { + meta = create_meta_ignore(line + 7, meta, META_IGNORE); + } else if (!strncasecmp("IgnoreContents:", line, 15)) { + meta = create_meta_ignore(line + 15, meta, + META_IGNORE_CONTENTS); + } else if (!strncasecmp("Include:", line, 8)) { + meta = create_meta_include(line + 8, meta); + } else if (!strncasecmp("Device:", line, 7)) { + meta = create_meta_device(line + 7, meta); + } else if (!strncasecmp("UserId:", line, 7)) { + meta = create_meta_uid(line + 7, meta); + } else if (!strncasecmp("GroupId:", line, 8)) { + meta = create_meta_gid(line + 8, meta); + } else if (!strncasecmp("DefaultUserId:", line, 14)) { + int count = sscanf(line + 14, "%u", default_uid_ptr); + if (count < 1) + fprintf(stderr, "Illegal default uid specification: %s", line); + } else if (!strncasecmp("DefaultGroupId:", line, 15)) { + int count = sscanf(line + 15, "%u", default_gid_ptr); + if (count < 1) + fprintf(stderr, "Illegal default gid specification: %s", line); + } else { + fprintf(stderr, "Illegal meta specification: %s", line); + } + } + } + + fclose(meta_file); + free(file_name); + + return meta; +} + +void free_meta_data(struct metadata *meta) +{ + struct metadata *next; + + for (; meta; meta = next) { + next = meta->next; + free(meta->name); + free(meta); + } +} + +int meta_ignore(struct metadata *meta, char *name) +{ + for (; meta; meta = meta->next) { + if (meta->type == META_IGNORE && !strcmp(name, meta->name)) { + return 1; + } + } + + return 0; +} + +int meta_ignore_contents(struct metadata *meta, char *name) +{ + for (; meta; meta = meta->next) { + if (meta->type == META_IGNORE_CONTENTS && !strcmp(name, meta->name)) { + return 1; + } + } + + return 0; +} + +int meta_include(struct metadata *meta, char *name) +{ + int found = 0; + + /* Always include . and .. */ + if (!strcmp(name, ".") || !strcmp(name, "..")) + return 1; + + for (; meta; meta = meta->next) { + if (meta->type == META_INCLUDE) { + found = 1; + if (!strcmp(name, meta->name)) { + return 1; + } + } + } + + return !found; +} + +int meta_handle_device(struct metadata *meta, struct entry *entry) +{ + for (; meta; meta = meta->next) { + if (meta->type == META_DEVICE && + !strcmp(entry->name, meta->name)) { + if (meta->dev_type == 'b') { + entry->mode = (entry->mode & ~S_IFMT) | S_IFBLK; + entry->size = makedev(meta->major, meta->minor); + } else if (meta->dev_type == 'c') { + entry->mode = (entry->mode & ~S_IFMT) | S_IFCHR; + entry->size = makedev(meta->major, meta->minor); + } else { + entry->mode = (entry->mode & ~S_IFMT) | S_IFIFO; + } + return 1; + } + } + + return 0; +} + +int meta_handle_uid(struct metadata *meta, struct entry *entry) +{ + for (; meta; meta = meta->next) { + if (meta->type == META_UID) { + if (!strcmp(entry->name, meta->name)) { + entry->uid = meta->id; + return 1; + } + } + } + + return 0; +} + +int meta_handle_gid(struct metadata *meta, struct entry *entry) +{ + for (; meta; meta = meta->next) { + if (meta->type == META_GID) { + if (!strcmp(entry->name, meta->name)) { + entry->gid = meta->id; + return 1; + } + } + } + + return 0; +} + +/*****************************************************************/ + /* * We define our own sorting function instead of using alphasort which * uses strcoll and changes ordering based on locale information. @@ -228,12 +576,21 @@ (*(const struct dirent **) b)->d_name); } -static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub) +static unsigned int parse_directory(struct entry *root_entry, + const char *name, + struct entry **prev, + loff_t *fslen_ub, + int ignore_contents, + unsigned int default_uid, + unsigned int default_gid) { struct dirent **dirlist; int totalsize = 0, dircount, dirindex; char *path, *endpath; size_t len = strlen(name); + struct metadata *meta; + + meta = read_meta_data(name, &default_uid, &default_gid); /* Set up the path. */ /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */ @@ -272,6 +629,29 @@ continue; } } + + /* Ignore files according to meta data */ + if (ignore_contents) { + printf("Meta: ignoring content %s/%s\n", + name + name_offset, dirent->d_name); + continue; + } + if (meta_file_name && !strcmp(dirent->d_name, meta_file_name)) { + printf("Meta: ignoring meta file %s/%s\n", + name + name_offset, dirent->d_name); + continue; /* don't include the meta data file */ + } + if (!meta_include(meta, dirent->d_name)) { + printf("Meta: not including %s/%s\n", + name + name_offset, dirent->d_name); + continue; /* include files according to the meta data */ + } + if (meta_ignore(meta, dirent->d_name)) { + printf("Meta: ignoring %s/%s\n", + name + name_offset, dirent->d_name); + continue; /* ignore files according to the meta data */ + } + namelen = strlen(dirent->d_name); if (namelen > MAX_INPUT_NAMELEN) { die(MKFS_ERROR, 0, @@ -330,8 +710,17 @@ size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3); *fslen_ub += size; if (S_ISDIR(st.st_mode)) { - entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub); + entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub, + meta_ignore_contents(meta, dirent->d_name), default_uid, default_gid); } else if (S_ISREG(st.st_mode)) { + if (meta_handle_device(meta, entry)) { + if (entry->size & -(1 << CRAMFS_SIZE_WIDTH)) + warn_dev = 1; + printf("Meta: device (%c %d %d) %s/%s\n", + (S_ISBLK(entry->mode))? 'b' : 'c', + major(entry->size), minor(entry->size), + name + name_offset, dirent->d_name); + } else { if (entry->size) { if (access(path, R_OK) < 0) { warn_skip = 1; @@ -346,6 +735,7 @@ entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1; } } + } } else if (S_ISLNK(st.st_mode)) { entry->uncompressed = malloc(entry->size); if (!entry->uncompressed) { @@ -374,11 +764,30 @@ *fslen_ub += (4+26)*blocks + entry->size + 3; } + if (meta_handle_uid(meta, entry)) { + printf("Meta: uid (%u) %s/%s\n", + entry->uid, name + name_offset, dirent->d_name); + } else if (default_uid != ID_NONE) { + entry->uid = default_uid; + printf("Meta: default uid (%u) %s/%s\n", + entry->uid, name + name_offset, dirent->d_name); + } + + if (meta_handle_gid(meta, entry)) { + printf("Meta: gid (%u) %s/%s\n", + entry->gid, name + name_offset, dirent->d_name); + } else if (default_gid != ID_NONE) { + entry->gid = default_gid; + printf("Meta: default gid (%u) %s/%s\n", + entry->gid, name + name_offset, dirent->d_name); + } + /* Link it into the list */ *prev = entry; prev = &entry->next; totalsize += size; } + free_meta_data(meta); free(path); free(dirlist); /* allocated by scandir() with malloc() */ return totalsize; @@ -716,7 +1125,7 @@ progname = argv[0]; /* command line options */ - while ((c = getopt(argc, argv, "hEe:i:b:n:psvz")) != EOF) { + while ((c = getopt(argc, argv, "hEe:i:b:m:n:psvz")) != EOF) { switch (c) { case 'h': usage(MKFS_OK); @@ -744,6 +1153,9 @@ die(MKFS_ERROR,0,"wrong block size\n"); } break; + case 'm': + meta_file_name = optarg; + break; case 'n': opt_name = optarg; break; @@ -769,6 +1181,9 @@ outfile = argv[optind + 1]; printf("Using a blocksize of %d bytes.\n", blksize); + if (meta_file_name) + printf("Using meta file(s) named \"%s\".\n", meta_file_name); + if (stat(dirname, &st) < 0) { die(MKFS_USAGE, 1, "stat failed: %s", dirname); } @@ -785,7 +1200,7 @@ root_entry->uid = st.st_uid; root_entry->gid = st.st_gid; - root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub); + root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub, 0, ID_NONE, ID_NONE); /* always allocate a multiple of blksize bytes because that's what we're going to write later on */