1 /* mkproto - make an mkfs prototype Author: Andrew Cagney */ 2 3 /* Submitted by: cagney@chook.ua.oz (Andrew Cagney - aka Noid) */ 4 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <minix/minlib.h> 8 #include <limits.h> 9 #include <dirent.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <stdio.h> 13 #include <unistd.h> 14 #include <assert.h> 15 #include <err.h> 16 17 /* The default values for the prototype file */ 18 #define DEF_UID 2 /* bin */ 19 #define DEF_GID 1 /* daemon group */ 20 #define DEF_PROT 0555 /* a=re */ 21 #define DEF_BLOCKS 360 22 #define DEF_INODES 63 23 #define DEF_INDENTSTR "\t" 24 25 #ifndef major 26 #define major(x) ( (x>>8) & 0377) 27 #define minor(x) (x & 0377) 28 #endif 29 30 /* Globals. */ 31 int count, origlen, tabs; 32 int gid, uid, prot, same_uid, same_gid, same_prot, blocks, inodes; 33 int block_given, inode_given, dprot; 34 char *indentstr; 35 char *proto_file, *top; 36 FILE *outfile; 37 38 extern int optind; 39 extern char *optarg; 40 41 int main(int argc, char **argv); 42 void descend(char *dirname); 43 void display_attrib(const char *name, struct stat *st); 44 void usage(char *binname); 45 void open_outfile(void); 46 47 /* Returned by minix_readdir */ 48 #define ME_MAXNAME 256 49 struct me_dirent { 50 char d_name[ME_MAXNAME]; 51 }; 52 53 struct me_dirent *minix_readdir(DIR *, int *n); 54 void minix_free_readdir(struct me_dirent *md, int n); 55 56 57 /* Compare dirent objects for order */ 58 static int cmp_dirent(const void *d1, const void *d2) 59 { 60 struct me_dirent *dp1 = (struct me_dirent *) d1, 61 *dp2 = (struct me_dirent *) d2; 62 return strcmp(dp1->d_name, dp2->d_name); 63 } 64 65 /* Return array of me_dirents. */ 66 struct me_dirent *minix_readdir(DIR *dirp, int *n) 67 { 68 struct dirent *rdp; 69 struct me_dirent *dp; 70 struct me_dirent *dirents = NULL; 71 int reserved_dirents = 0; 72 int entries = 0; 73 74 while((rdp = readdir(dirp)) != NULL) { 75 if(entries >= reserved_dirents) { 76 struct me_dirent *newdirents; 77 int newreserved = (2*(reserved_dirents+1)); 78 if(!(newdirents = realloc(dirents, newreserved * 79 sizeof(*dirents)))) { 80 free(dirents); 81 return NULL; 82 } 83 dirents = newdirents; 84 reserved_dirents = newreserved; 85 } 86 87 assert(entries < reserved_dirents); 88 assert(strlen(rdp->d_name) < sizeof(dp->d_name)); 89 dp = &dirents[entries]; 90 memset(dp, 0, sizeof(*dp)); 91 strcpy(dp->d_name, rdp->d_name); 92 entries++; 93 } 94 95 /* Assume directories contain at least "." and "..", and 96 * therefore the array exists. 97 */ 98 assert(entries > 0); 99 assert(dirents); 100 101 /* normalize (sort) them */ 102 qsort(dirents, entries, sizeof(*dp), cmp_dirent); 103 104 /* Return no. of entries. */ 105 *n = entries; 106 107 return dirents; 108 } 109 110 void minix_free_readdir(struct me_dirent *md, int n) 111 { 112 free(md); 113 } 114 115 int main(argc, argv) 116 int argc; 117 char *argv[]; 118 { 119 char *dir = __UNCONST(""); 120 struct stat st; 121 int op; 122 123 gid = DEF_GID; 124 uid = DEF_UID; 125 prot = DEF_PROT; 126 blocks = DEF_BLOCKS; 127 inodes = DEF_INODES; 128 indentstr = __UNCONST(DEF_INDENTSTR); 129 inode_given = 0; 130 block_given = 0; 131 top = 0; 132 same_uid = 0; 133 same_gid = 0; 134 same_prot = 0; 135 while ((op = getopt(argc, argv, "b:g:i:p:t:u:d:s")) != EOF) { 136 switch (op) { 137 case 'b': 138 blocks = atoi(optarg); 139 block_given = 1; 140 break; 141 case 'g': 142 gid = atoi(optarg); 143 if (gid == 0) usage(argv[0]); 144 same_gid = 0; 145 break; 146 case 'i': 147 inodes = atoi(optarg); 148 inode_given = 1; 149 break; 150 case 'p': 151 sscanf(optarg, "%o", &prot); 152 if (prot == 0) usage(argv[0]); 153 same_prot = 0; 154 break; 155 case 's': 156 same_prot = 1; 157 same_uid = 1; 158 same_gid = 1; 159 break; 160 case 't': top = optarg; break; 161 case 'u': 162 uid = atoi(optarg); 163 if (uid == 0) usage(argv[0]); 164 same_uid = 0; 165 break; 166 case 'd': indentstr = optarg; break; 167 default: /* Illegal options */ 168 usage(argv[0]); 169 } 170 } 171 172 if (optind >= argc) { 173 usage(argv[0]); 174 } else { 175 dir = argv[optind]; 176 optind++; 177 proto_file = argv[optind]; 178 } 179 if (!top) top = dir; 180 open_outfile(); 181 if (block_given && !inode_given) inodes = (blocks / 3) + 8; 182 if (!block_given && inode_given) usage(argv[0]); 183 count = 1; 184 tabs = 0; 185 origlen = strlen(dir); 186 187 /* Check that it really is a directory */ 188 stat(dir, &st); 189 if ((st.st_mode & S_IFMT) != S_IFDIR) { 190 fprintf(stderr, "mkproto: %s must be a directory\n", dir); 191 usage(argv[0]); 192 } 193 fprintf(outfile, "boot\n%d %d\n", blocks, inodes); 194 display_attrib("", &st); 195 fprintf(outfile, "\n"); 196 descend(dir); 197 fprintf(outfile, "$\n"); 198 return(0); 199 } 200 201 /* Output the prototype spec for this directory. */ 202 void descend(dirname) 203 char *dirname; 204 { 205 struct me_dirent *dirents; 206 DIR *dirp; 207 char *name, *temp, *tempend; 208 int i; 209 struct stat st; 210 mode_t mode; 211 int entries = 0, orig_entries; 212 struct me_dirent *dp; 213 214 dirp = opendir(dirname); 215 if (dirp == NULL) { 216 fprintf(stderr, "unable to open directory %s\n", dirname); 217 return; 218 } 219 tabs++; 220 temp = (char *) malloc(sizeof(char) * strlen(dirname) +1 + PATH_MAX); 221 strcpy(temp, dirname); 222 strcat(temp, "/"); 223 tempend = &temp[strlen(temp)]; 224 225 /* read all directory entries */ 226 if(!(dirents = minix_readdir(dirp, &entries))) 227 errx(1, "minix_readdir failed"); 228 orig_entries = entries; 229 closedir(dirp); 230 231 for (dp = dirents; entries > 0; dp++, entries--) { 232 name = dp->d_name; 233 234 count++; 235 strcpy(tempend, name); 236 237 if (lstat(temp, &st) == -1) { 238 fprintf(stderr, "cant get status of '%s' \n", temp); 239 continue; 240 } 241 if (name[0] == '.' && (name[1] == 0 || 242 (name[1] == '.' && name[2] == 0))) 243 continue; 244 245 display_attrib(name, &st); 246 247 mode = st.st_mode & S_IFMT; 248 if (mode == S_IFDIR) { 249 fprintf(outfile, "\n"); 250 descend(temp); 251 for (i = 0; i < tabs; i++) { 252 fprintf(outfile, "%s", indentstr); 253 } 254 fprintf(outfile, "$\n"); 255 continue; 256 } 257 if (mode == S_IFBLK || mode == S_IFCHR) { 258 fprintf(outfile, " %d %d\n", major(st.st_rdev), minor(st.st_rdev)); 259 continue; 260 } 261 if (mode == S_IFREG) { 262 i = origlen; 263 fprintf(outfile, "%s%s", indentstr, top); 264 while (temp[i] != '\0') { 265 fputc(temp[i], outfile); 266 i++; 267 } 268 fprintf(outfile, "\n"); 269 continue; 270 } 271 if (mode == S_IFLNK) { 272 char linkcontent[PATH_MAX]; 273 memset(linkcontent, 0, sizeof(linkcontent)); 274 if(readlink(temp, linkcontent, sizeof(linkcontent)) < 0) { 275 perror("readlink"); 276 exit(1); 277 } 278 fprintf(outfile, "%s%s\n", indentstr, linkcontent); 279 continue; 280 } 281 fprintf(outfile, " /dev/null"); 282 fprintf(stderr,"File\n\t%s\n has an invalid mode, made empty.\n",temp); 283 } 284 free(temp); 285 minix_free_readdir(dirents, orig_entries); 286 tabs--; 287 } 288 289 290 void display_attrib(name, st) 291 const char *name; 292 struct stat *st; 293 { 294 /* Output the specification for a single file */ 295 296 int i; 297 298 if (same_uid) uid = st->st_uid; 299 if (same_gid) gid = st->st_gid; 300 if (same_prot) 301 prot = st->st_mode & 0777; /***** This one is a bit shady *****/ 302 for (i = 0; i < tabs; i++) fprintf(outfile, "%s", indentstr); 303 fprintf(outfile, "%s%s%c%c%c%03o %d %d", 304 name, 305 *name == '\0' ? "" : indentstr, /* stop the tab for a null name */ 306 (st->st_mode & S_IFMT) == S_IFDIR ? 'd' : 307 (st->st_mode & S_IFMT) == S_IFCHR ? 'c' : 308 (st->st_mode & S_IFMT) == S_IFBLK ? 'b' : 309 (st->st_mode & S_IFMT) == S_IFLNK ? 's' : 310 '-', /* file type */ 311 (st->st_mode & S_ISUID) ? 'u' : '-', /* set uid */ 312 (st->st_mode & S_ISGID) ? 'g' : '-', /* set gid */ 313 prot, 314 uid, 315 gid); 316 } 317 318 void usage(binname) 319 char *binname; 320 { 321 fprintf(stderr, "Usage: %s [options] source_directory [prototype_file]\n", binname); 322 fprintf(stderr, "options:\n"); 323 fprintf(stderr, " -b n\t\t file system size is n blocks (default %d)\n", DEF_BLOCKS); 324 fprintf(stderr, " -d STRING\t define the indentation characters (default %s)\n", "(none)"); 325 fprintf(stderr, " -g n\t\t use n as the gid on all files (default %d)\n", DEF_GID); 326 fprintf(stderr, " -i n\t\t file system gets n i-nodes (default %d)\n", DEF_INODES); 327 fprintf(stderr, " -p nnn\t use nnn (octal) as mode on all files (default %o)\n", DEF_PROT); 328 fprintf(stderr, " -s \t\t use the same uid, gid and mode as originals have\n"); 329 fprintf(stderr, " -t ROOT\t inital path prefix for each entry\n"); 330 fprintf(stderr, " -u n\t\t use nnn as the uid on all files (default %d)\n", DEF_UID); 331 exit(1); 332 } 333 334 void open_outfile() 335 { 336 if (proto_file == NULL) 337 outfile = stdout; 338 else if ((outfile = fopen(proto_file, "w")) == NULL) 339 fprintf(stderr, "Cannot create %s\n ", proto_file); 340 } 341