mkfs.mfs, mkproto: minor features
[minix3.git] / usr.sbin / mkproto / mkproto.c
blob477d6dc71f9d90a0455ae23f617331459bbb47dd
1 /* mkproto - make an mkfs prototype Author: Andrew Cagney */
3 /* Submitted by: cagney@chook.ua.oz (Andrew Cagney - aka Noid) */
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>
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"
25 #ifndef major
26 #define major(x) ( (x>>8) & 0377)
27 #define minor(x) (x & 0377)
28 #endif
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;
38 extern int optind;
39 extern char *optarg;
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);
47 /* Returned by minix_readdir */
48 #define ME_MAXNAME 256
49 struct me_dirent {
50 char d_name[ME_MAXNAME];
53 struct me_dirent *minix_readdir(DIR *, int *n);
54 void minix_free_readdir(struct me_dirent *md, int n);
57 /* Compare dirent objects for order */
58 static int cmp_dirent(const void *d1, const void *d2)
60 struct me_dirent *dp1 = (struct me_dirent *) d1,
61 *dp2 = (struct me_dirent *) d2;
62 return strcmp(dp1->d_name, dp2->d_name);
65 /* Return array of me_dirents. */
66 struct me_dirent *minix_readdir(DIR *dirp, int *n)
68 struct dirent *rdp;
69 struct me_dirent *dp;
70 struct me_dirent *dirents = NULL;
71 int reserved_dirents = 0;
72 int entries = 0;
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;
83 dirents = newdirents;
84 reserved_dirents = newreserved;
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++;
95 /* Assume directories contain at least "." and "..", and
96 * therefore the array exists.
98 assert(entries > 0);
99 assert(dirents);
101 /* normalize (sort) them */
102 qsort(dirents, entries, sizeof(*dp), cmp_dirent);
104 /* Return no. of entries. */
105 *n = entries;
107 return dirents;
110 void minix_free_readdir(struct me_dirent *md, int n)
112 free(md);
115 int main(argc, argv)
116 int argc;
117 char *argv[];
119 char *dir = __UNCONST("");
120 struct stat st;
121 int op;
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]);
172 if (optind >= argc) {
173 usage(argv[0]);
174 } else {
175 dir = argv[optind];
176 optind++;
177 proto_file = argv[optind];
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);
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]);
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);
201 /* Output the prototype spec for this directory. */
202 void descend(dirname)
203 char *dirname;
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;
214 dirp = opendir(dirname);
215 if (dirp == NULL) {
216 fprintf(stderr, "unable to open directory %s\n", dirname);
217 return;
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)];
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);
231 for (dp = dirents; entries > 0; dp++, entries--) {
232 name = dp->d_name;
234 count++;
235 strcpy(tempend, name);
237 if (lstat(temp, &st) == -1) {
238 fprintf(stderr, "cant get status of '%s' \n", temp);
239 continue;
241 if (name[0] == '.' && (name[1] == 0 ||
242 (name[1] == '.' && name[2] == 0)))
243 continue;
245 display_attrib(name, &st);
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);
254 fprintf(outfile, "$\n");
255 continue;
257 if (mode == S_IFBLK || mode == S_IFCHR) {
258 fprintf(outfile, " %d %d\n", major(st.st_rdev), minor(st.st_rdev));
259 continue;
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++;
268 fprintf(outfile, "\n");
269 continue;
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);
278 fprintf(outfile, "%s%s\n", indentstr, linkcontent);
279 continue;
281 fprintf(outfile, " /dev/null");
282 fprintf(stderr,"File\n\t%s\n has an invalid mode, made empty.\n",temp);
284 free(temp);
285 minix_free_readdir(dirents, orig_entries);
286 tabs--;
290 void display_attrib(name, st)
291 const char *name;
292 struct stat *st;
294 /* Output the specification for a single file */
296 int i;
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);
318 void usage(binname)
319 char *binname;
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);
334 void open_outfile()
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);