xref: /minix3/minix/usr.sbin/mkproto/mkproto.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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 */
cmp_dirent(const void * d1,const void * d2)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. */
minix_readdir(DIR * dirp,int * n)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 
minix_free_readdir(struct me_dirent * md,int n)110 void minix_free_readdir(struct me_dirent *md, int n)
111 {
112        free(md);
113 }
114 
main(argc,argv)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. */
descend(dirname)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 
display_attrib(name,st)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 
usage(binname)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 
open_outfile()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