# # Patch managed by http://www.holgerschurig.de/patcher.html # --- genext2fs-1.3.orig/genext2fs.c~autosize.patch +++ genext2fs-1.3.orig/genext2fs.c @@ -4,6 +4,11 @@ // ext2 filesystem generator for embedded systems // Copyright (C) 2000 Xavier Bestel // +// 'du' portions taken from coreutils/du.c in busybox: +// Copyright (C) 1999,2000 by Lineo, inc. and John Beppu +// Copyright (C) 1999,2000,2001 by John Beppu +// Copyright (C) 2002 Edward Betts +// // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version @@ -79,9 +84,93 @@ #include #include #include +#include +#include + +#define HASH_SIZE 311 /* Should be prime */ +#define hash_inode(i) ((i) % HASH_SIZE) + +typedef struct ino_dev_hash_bucket_struct { + struct ino_dev_hash_bucket_struct *next; + ino_t ino; + dev_t dev; + char name[1]; +} ino_dev_hashtable_bucket_t; + +static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE]; + +struct stats { + unsigned long nblocks; + unsigned long ninodes; +}; + +int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name) +{ + ino_dev_hashtable_bucket_t *bucket; + + bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)]; + while (bucket != NULL) { + if ((bucket->ino == statbuf->st_ino) && + (bucket->dev == statbuf->st_dev)) + { + if (name) *name = bucket->name; + return 1; + } + bucket = bucket->next; + } + return 0; +} + +/* Add statbuf to statbuf hash table */ +void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) +{ + int i; + size_t s; + ino_dev_hashtable_bucket_t *bucket; + + i = hash_inode(statbuf->st_ino); + s = name ? strlen(name) : 0; + bucket = malloc(sizeof(ino_dev_hashtable_bucket_t) + s); + bucket->ino = statbuf->st_ino; + bucket->dev = statbuf->st_dev; + if (name) + strcpy(bucket->name, name); + else + bucket->name[0] = '\0'; + bucket->next = ino_dev_hashtable[i]; + ino_dev_hashtable[i] = bucket; +} + +/* Clear statbuf hash table */ +void reset_ino_dev_hashtable(void) +{ + int i; + ino_dev_hashtable_bucket_t *bucket; + + for (i = 0; i < HASH_SIZE; i++) { + while (ino_dev_hashtable[i] != NULL) { + bucket = ino_dev_hashtable[i]->next; + free(ino_dev_hashtable[i]); + ino_dev_hashtable[i] = bucket; + } + } +} +static int count_ino_in_hashtable(void) +{ + long count = 0; + int i; + for (i = 0; i < HASH_SIZE; i++) { + ino_dev_hashtable_bucket_t *bucket = ino_dev_hashtable[i]; + while (bucket != NULL) { + count++; + bucket = bucket->next; + } + } + return count; +} // block size @@ -1178,6 +1267,38 @@ return n; } +void stats_from_dir(struct stats *stats) +{ + DIR *dh; + struct dirent *dent; + struct stat st; + if(!(dh = opendir("."))) + perror_msg_and_die("."); + while((dent = readdir(dh))) + { + if((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) + continue; + lstat(dent->d_name, &st); + if (S_ISLNK(st.st_mode)) { + stats->ninodes++; + } else if (S_ISDIR(st.st_mode)) { + if(chdir(dent->d_name) < 0) + perror_msg_and_die(dent->d_name); + stats->ninodes++; + stats_from_dir(stats); + chdir(".."); + } else { + if (!is_in_ino_dev_hashtable(&st, NULL)) { + add_to_ino_dev_hashtable(&st, NULL); + stats->nblocks += (st.st_blocks >> 1); + stats->ninodes++; + } + } + } + closedir(dh); + reset_ino_dev_hashtable(); +} + // adds a tree of entries to the filesystem from current dir void add2fs_from_dir(filesystem *fs, uint32 this_nod) { @@ -1436,7 +1557,6 @@ free_blocks_per_group = nbblocks_per_group - overhead_per_group; } nbblocks = nbblocks_per_group * nbgroups + 1; - if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE))) error_msg_and_die("not enough memory for filesystem"); @@ -1891,6 +2011,7 @@ 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(filesystem *fs, char *line) { char type, *name = NULL, *tmp, *dir, *bname; @@ -2026,6 +2147,52 @@ return 0; } +static int stats_from_table_entry(char *line, struct stats *stats) +{ + char type, *name = NULL, *tmp, *dir, *bname; + unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; + unsigned long start = 0, increment = 1, count = 0; + inode *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"); + } + + tmp = xstrdup(name); + bname = xstrdup(basename(tmp)); + free(tmp); + switch (type) { + case 'd': + stats->ninodes++; + break; + case 'c': + case 'b': + if (count > 0) { + dev_t rdev; + char *dname; + unsigned long i; + for (i = start; i < count; i++) { + asprintf(&dname, "%s%lu", bname, i); + stats->ninodes++; + free(dname); + } + } else { + stats->ninodes++; + } + break; + } + free(bname); + free(name); + return 0; +} + static int parse_device_table(filesystem *root, FILE * file) { char *line; @@ -2070,6 +2237,45 @@ return status; } +static int stats_from_dev_table(FILE *file, struct stats *stats) +{ + char *line; + int status = 0; + size_t length = 0; + + /* Looks ok so far. The general plan now is to read in one + * line at a time, check for leading comment delimiters ('#'), + * then try and parse the line as a device table. If we fail + * to parse things, try and help the poor fool to fix their + * device table with a useful error msg... */ + line = NULL; + while (getline(&line, &length, file) != -1) { + /* First trim off any whitespace */ + int len = strlen(line); + + /* trim trailing whitespace */ + while (len > 0 && isspace(line[len - 1])) + line[--len] = '\0'; + /* trim leading whitespace */ + memmove(line, &line[strspn(line, " \n\r\t\v")], len); + + /* How long are we after trimming? */ + len = strlen(line); + + /* If this is NOT a comment line, try to interpret it */ + if (len && *line != '#') { + if (stats_from_table_entry(line, stats)) + status = 1; + } + + free(line); + line = NULL; + } + fclose(file); + + return status; +} + /* Local Variables: c-file-style: "linux" @@ -2112,6 +2318,8 @@ int nbblocks = -1; int nbinodes = -1; int nbresrvd = -1; + int tmp_nbblocks = -1; + int tmp_nbinodes = -1; char * fsout = "-"; char * fsin = 0; char * dopt[MAX_DOPT]; @@ -2128,6 +2336,7 @@ int c; struct stat sb; FILE *devtable = NULL; + struct stats stats; app_name = argv[0]; while((c = getopt(argc, argv, "x:d:b:i:r:g:e:zvhD:f:qUP")) != EOF) @@ -2184,6 +2393,7 @@ default: exit(1); } + if(optind < (argc - 1)) error_msg_and_die("too many arguments"); if(optind == (argc - 1)) @@ -2201,6 +2411,46 @@ } else { + stats.ninodes = 0; + stats.nblocks = 0; + for(i = 0; i < didx; i++) + { + struct stat st; + char *pdir; + stat(dopt[i], &st); + switch(st.st_mode & S_IFMT) + { + case S_IFDIR: + if(!(pdir = getcwd(0, GETCWD_SIZE))) + perror_msg_and_die(dopt[i]); + if(chdir(dopt[i]) < 0) + perror_msg_and_die(dopt[i]); + stats_from_dir(&stats); + if(chdir(pdir) < 0) + perror_msg_and_die(pdir); + free(pdir); + break; + default: + error_msg_and_die("%s is neither a file nor a directory", dopt[i]); + } + } + + if(devtable) + stats_from_dev_table(devtable, &stats); + + tmp_nbinodes = stats.ninodes + EXT2_FIRST_INO + 1; + tmp_nbblocks = stats.nblocks; + + if(tmp_nbblocks > nbblocks) + { + printf("Number of blocks too low, increasing to %d\n",tmp_nbblocks); + nbblocks = tmp_nbblocks; + } + if(tmp_nbinodes > nbinodes) + { + printf("Number of inodes too low, increasing to %d\n",tmp_nbinodes); + nbinodes = tmp_nbinodes; + } if(nbblocks == -1) error_msg_and_die("filesystem size unspecified"); if(nbinodes == -1)