xref: /minix3/minix/usr.sbin/mkfs.mfs/mkfs.c (revision cd34841de558a20be3f78f9ebee673dec2059333)
1433d6423SLionel Sambuc /* mkfs  -  make the MINIX filesystem	Authors: Tanenbaum et al. */
2433d6423SLionel Sambuc 
3433d6423SLionel Sambuc /*	Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans */
4433d6423SLionel Sambuc 
5433d6423SLionel Sambuc #if HAVE_NBTOOL_CONFIG_H
6433d6423SLionel Sambuc #include "nbtool_config.h"
7433d6423SLionel Sambuc #endif
8433d6423SLionel Sambuc 
9433d6423SLionel Sambuc #include <sys/cdefs.h>
10433d6423SLionel Sambuc #include <sys/types.h>
11433d6423SLionel Sambuc #include <sys/stat.h>
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #if defined(__minix)
14433d6423SLionel Sambuc #include <minix/minlib.h>
15433d6423SLionel Sambuc #include <minix/partition.h>
16433d6423SLionel Sambuc #include <sys/ioctl.h>
17433d6423SLionel Sambuc #elif defined(__linux__)
18433d6423SLionel Sambuc #include <mntent.h>
19433d6423SLionel Sambuc #endif
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc #include <assert.h>
22433d6423SLionel Sambuc #include <err.h>
23433d6423SLionel Sambuc #include <errno.h>
24433d6423SLionel Sambuc #include <fcntl.h>
25433d6423SLionel Sambuc #include <limits.h>
26433d6423SLionel Sambuc #include <stdarg.h>
27433d6423SLionel Sambuc #include <stdint.h>
28433d6423SLionel Sambuc #include <stdio.h>
29433d6423SLionel Sambuc #include <stdlib.h>
30433d6423SLionel Sambuc #include <string.h>
31433d6423SLionel Sambuc #include <time.h>
32433d6423SLionel Sambuc #include <unistd.h>
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc /* Definition of the file system layout: */
35433d6423SLionel Sambuc #include "const.h"
36433d6423SLionel Sambuc #include "type.h"
37433d6423SLionel Sambuc #include "mfsdir.h"
38433d6423SLionel Sambuc #include "super.h"
39433d6423SLionel Sambuc 
40433d6423SLionel Sambuc #define INODE_MAP	START_BLOCK
41433d6423SLionel Sambuc /* inode zone indexes pointing to single and double indirect zones */
42433d6423SLionel Sambuc #define S_INDIRECT_IDX	(NR_DZONES)
43433d6423SLionel Sambuc #define D_INDIRECT_IDX	(NR_DZONES+1)
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc 
46433d6423SLionel Sambuc #define MAX_TOKENS          10
47433d6423SLionel Sambuc #define LINE_LEN           200
48433d6423SLionel Sambuc /* XXX why do we not use 0 / SU_ID ? */
49433d6423SLionel Sambuc #define BIN                  2
50433d6423SLionel Sambuc #define BINGRP               2
51433d6423SLionel Sambuc 
52433d6423SLionel Sambuc /* some Minix specific types that do not conflict with Posix */
53433d6423SLionel Sambuc #ifndef block_t
54433d6423SLionel Sambuc typedef uint32_t block_t;	/* block number */
55433d6423SLionel Sambuc #endif
56433d6423SLionel Sambuc #ifndef zone_t
57433d6423SLionel Sambuc typedef uint32_t zone_t;	/* zone number */
58433d6423SLionel Sambuc #endif
59433d6423SLionel Sambuc #ifndef bit_t
60433d6423SLionel Sambuc typedef uint32_t bit_t;		/* bit number in a bit map */
61433d6423SLionel Sambuc #endif
62433d6423SLionel Sambuc #ifndef bitchunk_t
63433d6423SLionel Sambuc typedef uint32_t bitchunk_t;	/* collection of bits in a bitmap */
64433d6423SLionel Sambuc #endif
65433d6423SLionel Sambuc 
66433d6423SLionel Sambuc struct fs_size {
67433d6423SLionel Sambuc   ino_t inocount; /* amount of inodes */
68433d6423SLionel Sambuc   zone_t zonecount; /* amount of zones */
69433d6423SLionel Sambuc   block_t blockcount; /* amount of blocks */
70433d6423SLionel Sambuc };
71433d6423SLionel Sambuc 
72433d6423SLionel Sambuc extern char *optarg;
73433d6423SLionel Sambuc extern int optind;
74433d6423SLionel Sambuc 
75433d6423SLionel Sambuc block_t nrblocks;
76433d6423SLionel Sambuc int zone_per_block, zone_shift = 0;
77433d6423SLionel Sambuc zone_t next_zone, zoff, nr_indirzones;
78433d6423SLionel Sambuc int inodes_per_block, indir_per_block, indir_per_zone;
79433d6423SLionel Sambuc unsigned int zone_size;
80433d6423SLionel Sambuc ino_t nrinodes, inode_offset, next_inode;
81433d6423SLionel Sambuc int lct = 0, fd, print = 0;
82433d6423SLionel Sambuc int simple = 0, dflag = 0, verbose = 0;
83433d6423SLionel Sambuc int donttest;			/* skip test if it fits on medium */
84433d6423SLionel Sambuc char *progname;
85433d6423SLionel Sambuc uint64_t fs_offset_bytes, fs_offset_blocks, written_fs_size = 0;
86433d6423SLionel Sambuc 
87433d6423SLionel Sambuc time_t current_time;
88433d6423SLionel Sambuc char *zero;
89433d6423SLionel Sambuc unsigned char *umap_array;	/* bit map tells if block read yet */
90433d6423SLionel Sambuc size_t umap_array_elements;
91433d6423SLionel Sambuc block_t zone_map;		/* where is zone map? (depends on # inodes) */
92433d6423SLionel Sambuc #ifndef MFS_STATIC_BLOCK_SIZE
93433d6423SLionel Sambuc size_t block_size;
94433d6423SLionel Sambuc #else
95433d6423SLionel Sambuc #define block_size	MFS_STATIC_BLOCK_SIZE
96433d6423SLionel Sambuc #endif
97433d6423SLionel Sambuc 
98433d6423SLionel Sambuc FILE *proto;
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc int main(int argc, char **argv);
101433d6423SLionel Sambuc void detect_fs_size(struct fs_size * fssize);
102433d6423SLionel Sambuc void sizeup_dir(struct fs_size * fssize);
103433d6423SLionel Sambuc block_t sizeup(char *device);
104433d6423SLionel Sambuc static int bitmapsize(bit_t nr_bits, size_t blk_size);
105433d6423SLionel Sambuc void super(zone_t zones, ino_t inodes);
106433d6423SLionel Sambuc void rootdir(ino_t inode);
107433d6423SLionel Sambuc void enter_symlink(ino_t inode, char *link);
108433d6423SLionel Sambuc int dir_try_enter(zone_t z, ino_t child, char const *name);
109433d6423SLionel Sambuc void eat_dir(ino_t parent);
110433d6423SLionel Sambuc void eat_file(ino_t inode, int f);
111433d6423SLionel Sambuc void enter_dir(ino_t parent, char const *name, ino_t child);
112433d6423SLionel Sambuc void add_zone(ino_t n, zone_t z, size_t bytes, time_t cur_time);
113433d6423SLionel Sambuc void incr_link(ino_t n);
114433d6423SLionel Sambuc void incr_size(ino_t n, size_t count);
115433d6423SLionel Sambuc static ino_t alloc_inode(int mode, int usrid, int grpid);
116433d6423SLionel Sambuc static zone_t alloc_zone(void);
117433d6423SLionel Sambuc void insert_bit(block_t block, bit_t bit);
118433d6423SLionel Sambuc int mode_con(char *p);
119433d6423SLionel Sambuc void get_line(char line[LINE_LEN], char *parse[MAX_TOKENS]);
120433d6423SLionel Sambuc void check_mtab(const char *devname);
121433d6423SLionel Sambuc time_t file_time(int f);
122433d6423SLionel Sambuc __dead void pexit(char const *s, ...) __printflike(1,2);
123433d6423SLionel Sambuc void *alloc_block(void);
124433d6423SLionel Sambuc void print_fs(void);
125433d6423SLionel Sambuc int read_and_set(block_t n);
126433d6423SLionel Sambuc void special(char *string, int insertmode);
127433d6423SLionel Sambuc __dead void usage(void);
128433d6423SLionel Sambuc void get_block(block_t n, void *buf);
129433d6423SLionel Sambuc void get_super_block(void *buf);
130433d6423SLionel Sambuc void put_block(block_t n, void *buf);
131433d6423SLionel Sambuc static uint64_t mkfs_seek(uint64_t pos, int whence);
132433d6423SLionel Sambuc static ssize_t mkfs_write(void * buf, size_t count);
133433d6423SLionel Sambuc 
134433d6423SLionel Sambuc /*================================================================
135433d6423SLionel Sambuc  *                    mkfs  -  make filesystem
136433d6423SLionel Sambuc  *===============================================================*/
137433d6423SLionel Sambuc int
138433d6423SLionel Sambuc main(int argc, char *argv[])
139433d6423SLionel Sambuc {
140433d6423SLionel Sambuc   int nread, mode, usrid, grpid, ch, extra_space_percent, Tflag = 0;
141433d6423SLionel Sambuc   block_t blocks, maxblocks, bblocks;
142433d6423SLionel Sambuc   ino_t inodes, root_inum;
143433d6423SLionel Sambuc   char *token[MAX_TOKENS], line[LINE_LEN], *sfx;
144433d6423SLionel Sambuc   struct fs_size fssize;
145433d6423SLionel Sambuc   int insertmode = 0;
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc   progname = argv[0];
148433d6423SLionel Sambuc 
149433d6423SLionel Sambuc   /* Process switches. */
150433d6423SLionel Sambuc   blocks = 0;
151433d6423SLionel Sambuc   inodes = 0;
152433d6423SLionel Sambuc   bblocks = 0;
153433d6423SLionel Sambuc #ifndef MFS_STATIC_BLOCK_SIZE
154433d6423SLionel Sambuc   block_size = 0;
155433d6423SLionel Sambuc #endif
156433d6423SLionel Sambuc   zone_shift = 0;
157433d6423SLionel Sambuc   extra_space_percent = 0;
158433d6423SLionel Sambuc   while ((ch = getopt(argc, argv, "B:b:di:ltvx:z:I:T:")) != EOF)
159433d6423SLionel Sambuc 	switch (ch) {
160433d6423SLionel Sambuc #ifndef MFS_STATIC_BLOCK_SIZE
161433d6423SLionel Sambuc 	    case 'B':
162433d6423SLionel Sambuc 		block_size = strtoul(optarg, &sfx, 0);
163433d6423SLionel Sambuc 		switch(*sfx) {
164433d6423SLionel Sambuc 		case 'b': case 'B': /* bytes; NetBSD-compatible */
165433d6423SLionel Sambuc 		case '\0':	break;
166433d6423SLionel Sambuc 		case 'K':
167433d6423SLionel Sambuc 		case 'k':	block_size*=1024; break;
168433d6423SLionel Sambuc 		case 's': 	block_size*=SECTOR_SIZE; break;
169433d6423SLionel Sambuc 		default:	usage();
170433d6423SLionel Sambuc 		}
171433d6423SLionel Sambuc 		break;
172433d6423SLionel Sambuc #else
173433d6423SLionel Sambuc 	    case 'B':
174433d6423SLionel Sambuc 		if (block_size != strtoul(optarg, (char **) NULL, 0))
175433d6423SLionel Sambuc 			errx(4, "block size must be exactly %d bytes",
176433d6423SLionel Sambuc 			    MFS_STATIC_BLOCK_SIZE);
177433d6423SLionel Sambuc 		break;
178433d6423SLionel Sambuc 		(void)sfx;	/* shut up warnings about unused variable...*/
179433d6423SLionel Sambuc #endif
180433d6423SLionel Sambuc 	    case 'I':
181433d6423SLionel Sambuc 		fs_offset_bytes = strtoul(optarg, (char **) NULL, 0);
182433d6423SLionel Sambuc 		insertmode = 1;
183433d6423SLionel Sambuc 		break;
184433d6423SLionel Sambuc 	    case 'b':
185433d6423SLionel Sambuc 		blocks = bblocks = strtoul(optarg, (char **) NULL, 0);
186433d6423SLionel Sambuc 		break;
187433d6423SLionel Sambuc 	    case 'T':
188433d6423SLionel Sambuc 		Tflag = 1;
189433d6423SLionel Sambuc 		current_time = strtoul(optarg, (char **) NULL, 0);
190433d6423SLionel Sambuc 		break;
191433d6423SLionel Sambuc 	    case 'd':
192433d6423SLionel Sambuc 		dflag = 1;
193433d6423SLionel Sambuc 		break;
194433d6423SLionel Sambuc 	    case 'i':
195433d6423SLionel Sambuc 		inodes = strtoul(optarg, (char **) NULL, 0);
196433d6423SLionel Sambuc 		break;
197433d6423SLionel Sambuc 	    case 'l':	print = 1;	break;
198433d6423SLionel Sambuc 	    case 't':	donttest = 1;	break;
199433d6423SLionel Sambuc 	    case 'v':	++verbose;	break;
200433d6423SLionel Sambuc 	    case 'x':	extra_space_percent = atoi(optarg); break;
201433d6423SLionel Sambuc 	    case 'z':	zone_shift = atoi(optarg);	break;
202433d6423SLionel Sambuc 	    default:	usage();
203433d6423SLionel Sambuc 	}
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc   if (argc == optind) usage();
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc   /* Get the current time, set it to the mod time of the binary of
208433d6423SLionel Sambuc    * mkfs itself when the -d flag is used. The 'current' time is put into
209433d6423SLionel Sambuc    * the i_mtimes of all the files.  This -d feature is useful when
210433d6423SLionel Sambuc    * producing a set of file systems, and one wants all the times to be
211433d6423SLionel Sambuc    * identical. First you set the time of the mkfs binary to what you
212433d6423SLionel Sambuc    * want, then go.
213433d6423SLionel Sambuc    */
214433d6423SLionel Sambuc   if(Tflag) {
215433d6423SLionel Sambuc     if(dflag)
216433d6423SLionel Sambuc 	errx(1, "-T and -d both specify a time and so are mutually exclusive");
217433d6423SLionel Sambuc   } else if(dflag) {
218433d6423SLionel Sambuc 	struct stat statbuf;
219433d6423SLionel Sambuc 	if (stat(progname, &statbuf)) {
220433d6423SLionel Sambuc 		err(1, "stat of itself");
221433d6423SLionel Sambuc 	}
222433d6423SLionel Sambuc 	current_time = statbuf.st_mtime;
223433d6423SLionel Sambuc   } else {
224433d6423SLionel Sambuc 	  current_time = time((time_t *) 0);	/* time mkfs is being run */
225433d6423SLionel Sambuc   }
226433d6423SLionel Sambuc 
227433d6423SLionel Sambuc   /* Percentage of extra size must be nonnegative.
228433d6423SLionel Sambuc    * It can legitimately be bigger than 100 but has to make some sort of sense.
229433d6423SLionel Sambuc    */
230433d6423SLionel Sambuc   if(extra_space_percent < 0 || extra_space_percent > 2000) usage();
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc #ifdef DEFAULT_BLOCK_SIZE
233433d6423SLionel Sambuc   if(!block_size) block_size = DEFAULT_BLOCK_SIZE;
234433d6423SLionel Sambuc #endif
235433d6423SLionel Sambuc   if (block_size % SECTOR_SIZE)
236433d6423SLionel Sambuc 	errx(4, "block size must be multiple of sector (%d bytes)", SECTOR_SIZE);
237433d6423SLionel Sambuc #ifdef MIN_BLOCK_SIZE
238433d6423SLionel Sambuc   if (block_size < MIN_BLOCK_SIZE)
239433d6423SLionel Sambuc 	errx(4, "block size must be at least %d bytes", MIN_BLOCK_SIZE);
240433d6423SLionel Sambuc #endif
241433d6423SLionel Sambuc #ifdef MAX_BLOCK_SIZE
242433d6423SLionel Sambuc   if (block_size > MAX_BLOCK_SIZE)
243433d6423SLionel Sambuc 	errx(4, "block size must be at most %d bytes", MAX_BLOCK_SIZE);
244433d6423SLionel Sambuc #endif
245433d6423SLionel Sambuc   if(block_size%INODE_SIZE)
246433d6423SLionel Sambuc 	errx(4, "block size must be a multiple of inode size (%d bytes)", INODE_SIZE);
247433d6423SLionel Sambuc 
248433d6423SLionel Sambuc   if(zone_shift < 0 || zone_shift > 14)
249433d6423SLionel Sambuc 	errx(4, "zone_shift must be a small non-negative integer");
250433d6423SLionel Sambuc   zone_per_block = 1 << zone_shift;	/* nr of blocks per zone */
251433d6423SLionel Sambuc 
252433d6423SLionel Sambuc   inodes_per_block = INODES_PER_BLOCK(block_size);
253433d6423SLionel Sambuc   indir_per_block = INDIRECTS(block_size);
254433d6423SLionel Sambuc   indir_per_zone = INDIRECTS(block_size) << zone_shift;
255433d6423SLionel Sambuc   /* number of file zones we can address directly and with a single indirect*/
256433d6423SLionel Sambuc   nr_indirzones = NR_DZONES + indir_per_zone;
257433d6423SLionel Sambuc   zone_size = block_size << zone_shift;
258433d6423SLionel Sambuc   /* Checks for an overflow: only with very big block size */
259433d6423SLionel Sambuc   if (zone_size <= 0)
260433d6423SLionel Sambuc 	errx(4, "Zones are too big for this program; smaller -B or -z, please!");
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc   /* now that the block size is known, do buffer allocations where
263433d6423SLionel Sambuc    * possible.
264433d6423SLionel Sambuc    */
265433d6423SLionel Sambuc   zero = alloc_block();
266433d6423SLionel Sambuc 
267433d6423SLionel Sambuc   fs_offset_blocks = roundup(fs_offset_bytes, block_size) / block_size;
268433d6423SLionel Sambuc 
269433d6423SLionel Sambuc   /* Determine the size of the device if not specified as -b or proto. */
270433d6423SLionel Sambuc   maxblocks = sizeup(argv[optind]);
271433d6423SLionel Sambuc   if (bblocks != 0 && bblocks + fs_offset_blocks > maxblocks && !insertmode) {
272433d6423SLionel Sambuc 	errx(4, "Given size -b %d exeeds device capacity(%d)\n", bblocks, maxblocks);
273433d6423SLionel Sambuc   }
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc   if (argc - optind == 1 && bblocks == 0) {
276433d6423SLionel Sambuc   	blocks = maxblocks;
277433d6423SLionel Sambuc   	/* blocks == 0 is checked later, but leads to a funny way of
278433d6423SLionel Sambuc   	 * reporting a 0-sized device (displays usage).
279433d6423SLionel Sambuc   	 */
280433d6423SLionel Sambuc   	if(blocks < 1) {
281433d6423SLionel Sambuc   		errx(1, "zero size device.");
282433d6423SLionel Sambuc 	}
283433d6423SLionel Sambuc   }
284433d6423SLionel Sambuc 
285433d6423SLionel Sambuc   /* The remaining args must be 'special proto', or just 'special' if the
286433d6423SLionel Sambuc    * no. of blocks has already been specified.
287433d6423SLionel Sambuc    */
288433d6423SLionel Sambuc   if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage();
289433d6423SLionel Sambuc 
290433d6423SLionel Sambuc   if (maxblocks && blocks > maxblocks && !insertmode) {
291433d6423SLionel Sambuc  	errx(1, "%s: number of blocks too large for device.", argv[optind]);
292433d6423SLionel Sambuc   }
293433d6423SLionel Sambuc 
294433d6423SLionel Sambuc   /* Check special. */
295433d6423SLionel Sambuc   check_mtab(argv[optind]);
296433d6423SLionel Sambuc 
297433d6423SLionel Sambuc   /* Check and start processing proto. */
298433d6423SLionel Sambuc   optarg = argv[++optind];
299433d6423SLionel Sambuc   if (optind < argc && (proto = fopen(optarg, "r")) != NULL) {
300433d6423SLionel Sambuc 	/* Prototype file is readable. */
301433d6423SLionel Sambuc 	lct = 1;
302433d6423SLionel Sambuc 	get_line(line, token);	/* skip boot block info */
303433d6423SLionel Sambuc 
304433d6423SLionel Sambuc 	/* Read the line with the block and inode counts. */
305433d6423SLionel Sambuc 	get_line(line, token);
306433d6423SLionel Sambuc 	if (bblocks == 0){
307433d6423SLionel Sambuc 		blocks = strtol(token[0], (char **) NULL, 10);
308433d6423SLionel Sambuc 	} else {
309433d6423SLionel Sambuc 		if(bblocks < strtol(token[0], (char **) NULL, 10)) {
310433d6423SLionel Sambuc  			errx(1, "%s: number of blocks given as parameter(%d)"
311*cd34841dSBen Gras 				" is too small for given proto file(%ld).",
312433d6423SLionel Sambuc 				argv[optind], bblocks,
313433d6423SLionel Sambuc 				strtol(token[0], (char **) NULL, 10));
314433d6423SLionel Sambuc 		};
315433d6423SLionel Sambuc 	}
316433d6423SLionel Sambuc 	inodes = strtol(token[1], (char **) NULL, 10);
317433d6423SLionel Sambuc 
318433d6423SLionel Sambuc 	/* Process mode line for root directory. */
319433d6423SLionel Sambuc 	get_line(line, token);
320433d6423SLionel Sambuc 	mode = mode_con(token[0]);
321433d6423SLionel Sambuc 	usrid = atoi(token[1]);
322433d6423SLionel Sambuc 	grpid = atoi(token[2]);
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc 	if(blocks <= 0 && inodes <= 0){
325433d6423SLionel Sambuc 		detect_fs_size(&fssize);
326433d6423SLionel Sambuc 		blocks = fssize.blockcount;
327433d6423SLionel Sambuc 		inodes = fssize.inocount;
328433d6423SLionel Sambuc 		blocks += blocks*extra_space_percent/100;
329433d6423SLionel Sambuc 		inodes += inodes*extra_space_percent/100;
330433d6423SLionel Sambuc /* XXX is it OK to write on stdout? Use warn() instead? Also consider using verbose */
331433d6423SLionel Sambuc 		fprintf(stderr, "dynamically sized filesystem: %u blocks, %u inodes\n",
332433d6423SLionel Sambuc 		    (unsigned int) blocks, (unsigned int) inodes);
333433d6423SLionel Sambuc 	}
334433d6423SLionel Sambuc   } else {
335433d6423SLionel Sambuc 	lct = 0;
336433d6423SLionel Sambuc 	if (optind < argc) {
337433d6423SLionel Sambuc 		/* Maybe the prototype file is just a size.  Check. */
338433d6423SLionel Sambuc 		blocks = strtoul(optarg, (char **) NULL, 0);
339433d6423SLionel Sambuc 		if (blocks == 0) errx(2, "Can't open prototype file");
340433d6423SLionel Sambuc 	}
341433d6423SLionel Sambuc 
342433d6423SLionel Sambuc 	/* Make simple file system of the given size, using defaults. */
343433d6423SLionel Sambuc 	mode = 040777;
344433d6423SLionel Sambuc 	usrid = BIN;
345433d6423SLionel Sambuc 	grpid = BINGRP;
346433d6423SLionel Sambuc 	simple = 1;
347433d6423SLionel Sambuc   }
348433d6423SLionel Sambuc 
349433d6423SLionel Sambuc   if (inodes == 0) {
350433d6423SLionel Sambuc 	long long kb = ((unsigned long long)blocks*block_size) / 1024;
351433d6423SLionel Sambuc 
352433d6423SLionel Sambuc 	inodes = kb / 2;
353433d6423SLionel Sambuc 	if (kb >= 100000) inodes = kb / 4;
354433d6423SLionel Sambuc 	if (kb >= 1000000) inodes = kb / 6;
355433d6423SLionel Sambuc 	if (kb >= 10000000) inodes = kb / 8;
356433d6423SLionel Sambuc 	if (kb >= 100000000) inodes = kb / 10;
357433d6423SLionel Sambuc 	if (kb >= 1000000000) inodes = kb / 12;
358433d6423SLionel Sambuc /* XXX check overflow: with very large number of blocks, this results in insanely large number of inodes */
359433d6423SLionel Sambuc /* XXX check underflow (if/when ino_t is signed), else the message below will look strange */
360433d6423SLionel Sambuc 
361433d6423SLionel Sambuc 	/* round up to fill inode block */
362433d6423SLionel Sambuc 	inodes += inodes_per_block - 1;
363433d6423SLionel Sambuc 	inodes = inodes / inodes_per_block * inodes_per_block;
364433d6423SLionel Sambuc   }
365433d6423SLionel Sambuc 
366433d6423SLionel Sambuc   if (blocks < 5) errx(1, "Block count too small");
367433d6423SLionel Sambuc   if (inodes < 1) errx(1, "Inode count too small");
368433d6423SLionel Sambuc 
369433d6423SLionel Sambuc   nrblocks = blocks;
370433d6423SLionel Sambuc   nrinodes = inodes;
371433d6423SLionel Sambuc 
372433d6423SLionel Sambuc   umap_array_elements = 1 + blocks/8;
373433d6423SLionel Sambuc   if(!(umap_array = malloc(umap_array_elements)))
374433d6423SLionel Sambuc 	err(1, "can't allocate block bitmap (%u bytes).",
375433d6423SLionel Sambuc 		(unsigned)umap_array_elements);
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc   /* Open special. */
378433d6423SLionel Sambuc   special(argv[--optind], insertmode);
379433d6423SLionel Sambuc 
380433d6423SLionel Sambuc   if (!donttest) {
381433d6423SLionel Sambuc 	uint16_t *testb;
382433d6423SLionel Sambuc 	ssize_t w;
383433d6423SLionel Sambuc 
384433d6423SLionel Sambuc 	testb = alloc_block();
385433d6423SLionel Sambuc 
386433d6423SLionel Sambuc 	/* Try writing the last block of partition or diskette. */
387433d6423SLionel Sambuc 	mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET);
388433d6423SLionel Sambuc 	testb[0] = 0x3245;
389433d6423SLionel Sambuc 	testb[1] = 0x11FF;
390433d6423SLionel Sambuc 	testb[block_size/2-1] = 0x1F2F;
391433d6423SLionel Sambuc 	w=mkfs_write(testb, block_size);
392433d6423SLionel Sambuc 	sync();			/* flush write, so if error next read fails */
393433d6423SLionel Sambuc 	mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET);
394433d6423SLionel Sambuc 	testb[0] = 0;
395433d6423SLionel Sambuc 	testb[1] = 0;
396433d6423SLionel Sambuc 	testb[block_size/2-1] = 0;
397433d6423SLionel Sambuc 	nread = read(fd, testb, block_size);
398433d6423SLionel Sambuc 	if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF ||
399433d6423SLionel Sambuc 	    testb[block_size/2-1] != 0x1F2F) {
400433d6423SLionel Sambuc 		warn("nread = %d\n", nread);
401433d6423SLionel Sambuc 		warnx("testb = 0x%x 0x%x 0x%x\n",
402433d6423SLionel Sambuc 		    testb[0], testb[1], testb[block_size-1]);
403433d6423SLionel Sambuc 		errx(1, "File system is too big for minor device (read)");
404433d6423SLionel Sambuc 	}
405433d6423SLionel Sambuc 	mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET);
406433d6423SLionel Sambuc 	testb[0] = 0;
407433d6423SLionel Sambuc 	testb[1] = 0;
408433d6423SLionel Sambuc 	testb[block_size/2-1] = 0;
409433d6423SLionel Sambuc 	mkfs_write(testb, block_size);
410433d6423SLionel Sambuc 	mkfs_seek(0L, SEEK_SET);
411433d6423SLionel Sambuc 	free(testb);
412433d6423SLionel Sambuc   }
413433d6423SLionel Sambuc 
414433d6423SLionel Sambuc   /* Make the file-system */
415433d6423SLionel Sambuc 
416433d6423SLionel Sambuc   put_block(BOOT_BLOCK, zero);		/* Write a null boot block. */
417433d6423SLionel Sambuc   put_block(BOOT_BLOCK+1, zero);	/* Write another null block. */
418433d6423SLionel Sambuc 
419433d6423SLionel Sambuc   super(nrblocks >> zone_shift, inodes);
420433d6423SLionel Sambuc 
421433d6423SLionel Sambuc   root_inum = alloc_inode(mode, usrid, grpid);
422433d6423SLionel Sambuc   rootdir(root_inum);
423433d6423SLionel Sambuc   if (simple == 0) eat_dir(root_inum);
424433d6423SLionel Sambuc 
425433d6423SLionel Sambuc   if (print) print_fs();
426433d6423SLionel Sambuc   else if (verbose > 1) {
427433d6423SLionel Sambuc 	  if (zone_shift)
428433d6423SLionel Sambuc 		fprintf(stderr, "%d inodes used.     %u zones (%u blocks) used.\n",
429433d6423SLionel Sambuc 		    (int)next_inode-1, next_zone, next_zone*zone_per_block);
430433d6423SLionel Sambuc 	  else
431433d6423SLionel Sambuc 		fprintf(stderr, "%d inodes used.     %u zones used.\n",
432433d6423SLionel Sambuc 		    (int)next_inode-1, next_zone);
433433d6423SLionel Sambuc   }
434433d6423SLionel Sambuc 
435*cd34841dSBen Gras   if(insertmode) printf("%llu\n", written_fs_size);
436433d6423SLionel Sambuc 
437433d6423SLionel Sambuc   return(0);
438433d6423SLionel Sambuc 
439433d6423SLionel Sambuc   /* NOTREACHED */
440433d6423SLionel Sambuc }				/* end main */
441433d6423SLionel Sambuc 
442433d6423SLionel Sambuc /*================================================================
443433d6423SLionel Sambuc  *        detect_fs_size  -  determine image size dynamically
444433d6423SLionel Sambuc  *===============================================================*/
445433d6423SLionel Sambuc void
446433d6423SLionel Sambuc detect_fs_size(struct fs_size * fssize)
447433d6423SLionel Sambuc {
448433d6423SLionel Sambuc   int prev_lct = lct;
449433d6423SLionel Sambuc   off_t point = ftell(proto);
450433d6423SLionel Sambuc   block_t initb;
451433d6423SLionel Sambuc   zone_t initz;
452433d6423SLionel Sambuc 
453433d6423SLionel Sambuc   fssize->inocount = 1;	/* root directory node */
454433d6423SLionel Sambuc   fssize->zonecount = 0;
455433d6423SLionel Sambuc   fssize->blockcount = 0;
456433d6423SLionel Sambuc 
457433d6423SLionel Sambuc   sizeup_dir(fssize);
458433d6423SLionel Sambuc 
459433d6423SLionel Sambuc   initb = bitmapsize(1 + fssize->inocount, block_size);
460433d6423SLionel Sambuc   initb += bitmapsize(fssize->zonecount, block_size);
461433d6423SLionel Sambuc   initb += START_BLOCK;
462433d6423SLionel Sambuc   initb += (fssize->inocount + inodes_per_block - 1) / inodes_per_block;
463433d6423SLionel Sambuc   initz = (initb + zone_per_block - 1) >> zone_shift;
464433d6423SLionel Sambuc 
465433d6423SLionel Sambuc   fssize->blockcount = initb+ fssize->zonecount;
466433d6423SLionel Sambuc   lct = prev_lct;
467433d6423SLionel Sambuc   fseek(proto, point, SEEK_SET);
468433d6423SLionel Sambuc }
469433d6423SLionel Sambuc 
470433d6423SLionel Sambuc void
471433d6423SLionel Sambuc sizeup_dir(struct fs_size * fssize)
472433d6423SLionel Sambuc {
473433d6423SLionel Sambuc   char *token[MAX_TOKENS], *p;
474433d6423SLionel Sambuc   char line[LINE_LEN];
475433d6423SLionel Sambuc   FILE *f;
476433d6423SLionel Sambuc   off_t size;
477433d6423SLionel Sambuc   int dir_entries = 2;
478433d6423SLionel Sambuc   zone_t dir_zones = 0, fzones, indirects;
479433d6423SLionel Sambuc 
480433d6423SLionel Sambuc   while (1) {
481433d6423SLionel Sambuc 	get_line(line, token);
482433d6423SLionel Sambuc 	p = token[0];
483433d6423SLionel Sambuc 	if (*p == '$') {
484433d6423SLionel Sambuc 		dir_zones = (dir_entries / (NR_DIR_ENTRIES(block_size) * zone_per_block));
485433d6423SLionel Sambuc 		if(dir_entries % (NR_DIR_ENTRIES(block_size) * zone_per_block))
486433d6423SLionel Sambuc 			dir_zones++;
487433d6423SLionel Sambuc 		if(dir_zones > NR_DZONES)
488433d6423SLionel Sambuc 			dir_zones++;	/* Max single indir */
489433d6423SLionel Sambuc 		fssize->zonecount += dir_zones;
490433d6423SLionel Sambuc 		return;
491433d6423SLionel Sambuc 	}
492433d6423SLionel Sambuc 
493433d6423SLionel Sambuc 	p = token[1];
494433d6423SLionel Sambuc 	fssize->inocount++;
495433d6423SLionel Sambuc 	dir_entries++;
496433d6423SLionel Sambuc 
497433d6423SLionel Sambuc 	if (*p == 'd') {
498433d6423SLionel Sambuc 		sizeup_dir(fssize);
499433d6423SLionel Sambuc 	} else if (*p == 'b' || *p == 'c') {
500433d6423SLionel Sambuc 
501433d6423SLionel Sambuc 	} else if (*p == 's') {
502433d6423SLionel Sambuc 		fssize->zonecount++; /* Symlink contents is always stored a block */
503433d6423SLionel Sambuc 	} else {
504433d6423SLionel Sambuc 		if ((f = fopen(token[4], "rb")) == NULL) {
505*cd34841dSBen Gras /* on minix natively, allow EACCES and skip the entry.
506*cd34841dSBen Gras  * while crossbuilding, always fail on error.
507*cd34841dSBen Gras  */
508*cd34841dSBen Gras #ifdef __minix
509*cd34841dSBen Gras 			if(errno == EACCES)
510433d6423SLionel Sambuc 				warn("dynamic sizing: can't open %s", token[4]);
511*cd34841dSBen Gras 			else
512*cd34841dSBen Gras #endif
513*cd34841dSBen Gras 				err(1, "dynamic sizing: can't open %s", token[4]);
514433d6423SLionel Sambuc 		} else if (fseek(f, 0, SEEK_END) < 0) {
515433d6423SLionel Sambuc 			pexit("dynamic size detection failed: seek to end of %s",
516433d6423SLionel Sambuc 			    token[4]);
517433d6423SLionel Sambuc 		} else if ( (size = ftell(f)) == (off_t)(-1)) {
518433d6423SLionel Sambuc 			pexit("dynamic size detection failed: can't tell size of %s",
519433d6423SLionel Sambuc 			    token[4]);
520433d6423SLionel Sambuc 		} else {
521433d6423SLionel Sambuc 			fclose(f);
522433d6423SLionel Sambuc 			fzones = roundup(size, zone_size) / zone_size;
523433d6423SLionel Sambuc 			indirects = 0;
524433d6423SLionel Sambuc 			/* XXX overflow? fzones is u32, size is potentially 64-bit */
525433d6423SLionel Sambuc 			if (fzones > NR_DZONES)
526433d6423SLionel Sambuc 				indirects++; /* single indirect needed */
527433d6423SLionel Sambuc 			if (fzones > nr_indirzones) {
528433d6423SLionel Sambuc 				/* Each further group of 'indir_per_zone'
529433d6423SLionel Sambuc 				 * needs one supplementary indirect zone:
530433d6423SLionel Sambuc 				 */
531433d6423SLionel Sambuc 				indirects += roundup(fzones - nr_indirzones,
532433d6423SLionel Sambuc 				    indir_per_zone) / indir_per_zone;
533433d6423SLionel Sambuc 				indirects++;	/* + double indirect needed!*/
534433d6423SLionel Sambuc 			}
535433d6423SLionel Sambuc 			fssize->zonecount += fzones + indirects;
536433d6423SLionel Sambuc 		}
537433d6423SLionel Sambuc 	}
538433d6423SLionel Sambuc   }
539433d6423SLionel Sambuc }
540433d6423SLionel Sambuc 
541433d6423SLionel Sambuc /*================================================================
542433d6423SLionel Sambuc  *                    sizeup  -  determine device size
543433d6423SLionel Sambuc  *===============================================================*/
544433d6423SLionel Sambuc block_t
545433d6423SLionel Sambuc sizeup(char * device)
546433d6423SLionel Sambuc {
547433d6423SLionel Sambuc   block_t d;
548433d6423SLionel Sambuc #if defined(__minix)
549433d6423SLionel Sambuc   uint64_t bytes, resize;
550433d6423SLionel Sambuc   uint32_t rem;
551433d6423SLionel Sambuc #else
552433d6423SLionel Sambuc   off_t size;
553433d6423SLionel Sambuc #endif
554433d6423SLionel Sambuc 
555433d6423SLionel Sambuc 
556433d6423SLionel Sambuc   if ((fd = open(device, O_RDONLY)) == -1) {
557433d6423SLionel Sambuc 	if (errno != ENOENT)
558433d6423SLionel Sambuc 		perror("sizeup open");
559433d6423SLionel Sambuc 	return 0;
560433d6423SLionel Sambuc   }
561433d6423SLionel Sambuc 
562433d6423SLionel Sambuc #if defined(__minix)
563433d6423SLionel Sambuc   if(minix_sizeup(device, &bytes) < 0) {
564433d6423SLionel Sambuc        perror("sizeup");
565433d6423SLionel Sambuc        return 0;
566433d6423SLionel Sambuc   }
567433d6423SLionel Sambuc 
568433d6423SLionel Sambuc   d = (uint32_t)(bytes / block_size);
569433d6423SLionel Sambuc   rem = (uint32_t)(bytes % block_size);
570433d6423SLionel Sambuc 
571433d6423SLionel Sambuc   resize = ((uint64_t)d * block_size) + rem;
572433d6423SLionel Sambuc   if(resize != bytes) {
573433d6423SLionel Sambuc 	/* Assume block_t is unsigned */
574433d6423SLionel Sambuc 	d = (block_t)(-1ul);
575433d6423SLionel Sambuc 	fprintf(stderr, "%s: truncating FS at %lu blocks\n",
576433d6423SLionel Sambuc 		progname, (unsigned long)d);
577433d6423SLionel Sambuc   }
578433d6423SLionel Sambuc #else
579433d6423SLionel Sambuc   size = mkfs_seek(0, SEEK_END);
580433d6423SLionel Sambuc   /* Assume block_t is unsigned */
581433d6423SLionel Sambuc   if (size / block_size > (block_t)(-1ul)) {
582433d6423SLionel Sambuc 	d = (block_t)(-1ul);
583433d6423SLionel Sambuc 	fprintf(stderr, "%s: truncating FS at %lu blocks\n",
584433d6423SLionel Sambuc 		progname, (unsigned long)d);
585433d6423SLionel Sambuc   } else
586433d6423SLionel Sambuc 	d = size / block_size;
587433d6423SLionel Sambuc #endif
588433d6423SLionel Sambuc 
589433d6423SLionel Sambuc   return d;
590433d6423SLionel Sambuc }
591433d6423SLionel Sambuc 
592433d6423SLionel Sambuc /*
593433d6423SLionel Sambuc  * copied from fslib
594433d6423SLionel Sambuc  */
595433d6423SLionel Sambuc static int
596433d6423SLionel Sambuc bitmapsize(bit_t nr_bits, size_t blk_size)
597433d6423SLionel Sambuc {
598433d6423SLionel Sambuc   block_t nr_blocks;
599433d6423SLionel Sambuc 
600433d6423SLionel Sambuc   nr_blocks = nr_bits / FS_BITS_PER_BLOCK(blk_size);
601433d6423SLionel Sambuc   if (nr_blocks * FS_BITS_PER_BLOCK(blk_size) < nr_bits)
602433d6423SLionel Sambuc 	++nr_blocks;
603433d6423SLionel Sambuc   return(nr_blocks);
604433d6423SLionel Sambuc }
605433d6423SLionel Sambuc 
606433d6423SLionel Sambuc /*================================================================
607433d6423SLionel Sambuc  *                 super  -  construct a superblock
608433d6423SLionel Sambuc  *===============================================================*/
609433d6423SLionel Sambuc 
610433d6423SLionel Sambuc void
611433d6423SLionel Sambuc super(zone_t zones, ino_t inodes)
612433d6423SLionel Sambuc {
613433d6423SLionel Sambuc   block_t inodeblks, initblks, i;
614433d6423SLionel Sambuc   unsigned long nb;
615433d6423SLionel Sambuc   long long ind_per_zone, zo;
616433d6423SLionel Sambuc   void *buf;
617433d6423SLionel Sambuc   struct super_block *sup;
618433d6423SLionel Sambuc 
619433d6423SLionel Sambuc   sup = buf = alloc_block();
620433d6423SLionel Sambuc 
621433d6423SLionel Sambuc #ifdef MFSFLAG_CLEAN
622433d6423SLionel Sambuc   /* The assumption is that mkfs will create a clean FS. */
623433d6423SLionel Sambuc   sup->s_flags = MFSFLAG_CLEAN;
624433d6423SLionel Sambuc #endif
625433d6423SLionel Sambuc 
626433d6423SLionel Sambuc   sup->s_ninodes = inodes;
627433d6423SLionel Sambuc   /* Check for overflow; cannot happen on V3 file systems */
628433d6423SLionel Sambuc   if(inodes != sup->s_ninodes)
629433d6423SLionel Sambuc 	errx(1, "Too much inodes for that version of Minix FS.");
630433d6423SLionel Sambuc   sup->s_nzones = 0;	/* not used in V2 - 0 forces errors early */
631433d6423SLionel Sambuc   sup->s_zones = zones;
632433d6423SLionel Sambuc   /* Check for overflow; can only happen on V1 file systems */
633433d6423SLionel Sambuc   if(zones != sup->s_zones)
634433d6423SLionel Sambuc 	errx(1, "Too much zones (blocks) for that version of Minix FS.");
635433d6423SLionel Sambuc 
636433d6423SLionel Sambuc #ifndef MFS_STATIC_BLOCK_SIZE
637433d6423SLionel Sambuc #define BIGGERBLOCKS "Please try a larger block size for an FS of this size."
638433d6423SLionel Sambuc #else
639433d6423SLionel Sambuc #define BIGGERBLOCKS "Please use MinixFS V3 for an FS of this size."
640433d6423SLionel Sambuc #endif
641433d6423SLionel Sambuc   sup->s_imap_blocks = nb = bitmapsize(1 + inodes, block_size);
642433d6423SLionel Sambuc   /* Checks for an overflow: nb is uint32_t while s_imap_blocks is of type
643433d6423SLionel Sambuc    * int16_t */
644433d6423SLionel Sambuc   if(sup->s_imap_blocks != nb) {
645433d6423SLionel Sambuc 	errx(1, "too many inode bitmap blocks.\n" BIGGERBLOCKS);
646433d6423SLionel Sambuc   }
647433d6423SLionel Sambuc   sup->s_zmap_blocks = nb = bitmapsize(zones, block_size);
648433d6423SLionel Sambuc   /* Idem here check for overflow */
649433d6423SLionel Sambuc   if(nb != sup->s_zmap_blocks) {
650433d6423SLionel Sambuc 	errx(1, "too many block bitmap blocks.\n" BIGGERBLOCKS);
651433d6423SLionel Sambuc   }
652433d6423SLionel Sambuc   inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks;
653433d6423SLionel Sambuc   inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block;
654433d6423SLionel Sambuc   initblks = inode_offset + inodeblks;
655433d6423SLionel Sambuc   sup->s_firstdatazone_old = nb =
656433d6423SLionel Sambuc 	(initblks + (1 << zone_shift) - 1) >> zone_shift;
657433d6423SLionel Sambuc   if(nb >= zones) errx(1, "bit maps too large");
658433d6423SLionel Sambuc   if(nb != sup->s_firstdatazone_old) {
659433d6423SLionel Sambuc 	/* The field is too small to store the value. Fortunately, the value
660433d6423SLionel Sambuc 	 * can be computed from other fields. We set the on-disk field to zero
661433d6423SLionel Sambuc 	 * to indicate that it must not be used. Eventually, we can always set
662433d6423SLionel Sambuc 	 * the on-disk field to zero, and stop using it.
663433d6423SLionel Sambuc 	 */
664433d6423SLionel Sambuc 	sup->s_firstdatazone_old = 0;
665433d6423SLionel Sambuc   }
666433d6423SLionel Sambuc   sup->s_firstdatazone = nb;
667433d6423SLionel Sambuc   zoff = sup->s_firstdatazone - 1;
668433d6423SLionel Sambuc   sup->s_log_zone_size = zone_shift;
669433d6423SLionel Sambuc   sup->s_magic = SUPER_MAGIC;
670433d6423SLionel Sambuc #ifdef MFS_SUPER_BLOCK_SIZE
671433d6423SLionel Sambuc   sup->s_block_size = block_size;
672433d6423SLionel Sambuc   /* Check for overflow */
673433d6423SLionel Sambuc   if(block_size != sup->MFS_SUPER_BLOCK_SIZE)
674433d6423SLionel Sambuc 	errx(1, "block_size too large.");
675433d6423SLionel Sambuc   sup->s_disk_version = 0;
676433d6423SLionel Sambuc #endif
677433d6423SLionel Sambuc 
678433d6423SLionel Sambuc   ind_per_zone = (long long) indir_per_zone;
679433d6423SLionel Sambuc   zo = NR_DZONES + ind_per_zone + ind_per_zone*ind_per_zone;
680433d6423SLionel Sambuc #ifndef MAX_MAX_SIZE
681433d6423SLionel Sambuc #define MAX_MAX_SIZE 	(INT32_MAX)
682433d6423SLionel Sambuc #endif
683433d6423SLionel Sambuc   if(MAX_MAX_SIZE/block_size < zo) {
684433d6423SLionel Sambuc 	sup->s_max_size = MAX_MAX_SIZE;
685433d6423SLionel Sambuc   }
686433d6423SLionel Sambuc   else {
687433d6423SLionel Sambuc 	sup->s_max_size = zo * block_size;
688433d6423SLionel Sambuc   }
689433d6423SLionel Sambuc 
690433d6423SLionel Sambuc   if (verbose>1) {
691433d6423SLionel Sambuc 	fprintf(stderr, "Super block values:\n"
692433d6423SLionel Sambuc 	    "\tnumber of inodes\t%12d\n"
693433d6423SLionel Sambuc 	    "\tnumber of zones \t%12d\n"
694433d6423SLionel Sambuc 	    "\tinode bit map blocks\t%12d\n"
695433d6423SLionel Sambuc 	    "\tzone bit map blocks\t%12d\n"
696433d6423SLionel Sambuc 	    "\tfirst data zone \t%12d\n"
697433d6423SLionel Sambuc 	    "\tblocks per zone shift\t%12d\n"
698433d6423SLionel Sambuc 	    "\tmaximum file size\t%12d\n"
699433d6423SLionel Sambuc 	    "\tmagic number\t\t%#12X\n",
700433d6423SLionel Sambuc 	    sup->s_ninodes, sup->s_zones,
701433d6423SLionel Sambuc 	    sup->s_imap_blocks, sup->s_zmap_blocks, sup->s_firstdatazone,
702433d6423SLionel Sambuc 	    sup->s_log_zone_size, sup->s_max_size, sup->s_magic);
703433d6423SLionel Sambuc #ifdef MFS_SUPER_BLOCK_SIZE
704433d6423SLionel Sambuc 	fprintf(stderr, "\tblock size\t\t%12d\n", sup->s_block_size);
705433d6423SLionel Sambuc #endif
706433d6423SLionel Sambuc   }
707433d6423SLionel Sambuc 
708433d6423SLionel Sambuc   mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET);
709433d6423SLionel Sambuc   mkfs_write(buf, SUPER_BLOCK_BYTES);
710433d6423SLionel Sambuc 
711433d6423SLionel Sambuc   /* Clear maps and inodes. */
712433d6423SLionel Sambuc   for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero);
713433d6423SLionel Sambuc 
714433d6423SLionel Sambuc   next_zone = sup->s_firstdatazone;
715433d6423SLionel Sambuc   next_inode = 1;
716433d6423SLionel Sambuc 
717433d6423SLionel Sambuc   zone_map = INODE_MAP + sup->s_imap_blocks;
718433d6423SLionel Sambuc 
719433d6423SLionel Sambuc   insert_bit(zone_map, 0);	/* bit zero must always be allocated */
720433d6423SLionel Sambuc   insert_bit((block_t) INODE_MAP, 0);	/* inode zero not used but
721433d6423SLionel Sambuc 					 * must be allocated */
722433d6423SLionel Sambuc 
723433d6423SLionel Sambuc   free(buf);
724433d6423SLionel Sambuc }
725433d6423SLionel Sambuc 
726433d6423SLionel Sambuc 
727433d6423SLionel Sambuc /*================================================================
728433d6423SLionel Sambuc  *              rootdir  -  install the root directory
729433d6423SLionel Sambuc  *===============================================================*/
730433d6423SLionel Sambuc void
731433d6423SLionel Sambuc rootdir(ino_t inode)
732433d6423SLionel Sambuc {
733433d6423SLionel Sambuc   zone_t z;
734433d6423SLionel Sambuc 
735433d6423SLionel Sambuc   z = alloc_zone();
736433d6423SLionel Sambuc   add_zone(inode, z, 2 * sizeof(struct direct), current_time);
737433d6423SLionel Sambuc   enter_dir(inode, ".", inode);
738433d6423SLionel Sambuc   enter_dir(inode, "..", inode);
739433d6423SLionel Sambuc   incr_link(inode);
740433d6423SLionel Sambuc   incr_link(inode);
741433d6423SLionel Sambuc }
742433d6423SLionel Sambuc 
743433d6423SLionel Sambuc void
744433d6423SLionel Sambuc enter_symlink(ino_t inode, char *lnk)
745433d6423SLionel Sambuc {
746433d6423SLionel Sambuc   zone_t z;
747433d6423SLionel Sambuc   size_t len;
748433d6423SLionel Sambuc   char *buf;
749433d6423SLionel Sambuc 
750433d6423SLionel Sambuc   buf = alloc_block();
751433d6423SLionel Sambuc   z = alloc_zone();
752433d6423SLionel Sambuc   len = strlen(lnk);
753433d6423SLionel Sambuc   if (len >= block_size)
754433d6423SLionel Sambuc 	pexit("symlink too long, max length is %u", (unsigned)block_size - 1);
755433d6423SLionel Sambuc   strcpy(buf, lnk);
756433d6423SLionel Sambuc   put_block((z << zone_shift), buf);
757433d6423SLionel Sambuc 
758433d6423SLionel Sambuc   add_zone(inode, z, len, current_time);
759433d6423SLionel Sambuc 
760433d6423SLionel Sambuc   free(buf);
761433d6423SLionel Sambuc }
762433d6423SLionel Sambuc 
763433d6423SLionel Sambuc 
764433d6423SLionel Sambuc /*================================================================
765433d6423SLionel Sambuc  *	    eat_dir  -  recursively install directory
766433d6423SLionel Sambuc  *===============================================================*/
767433d6423SLionel Sambuc void
768433d6423SLionel Sambuc eat_dir(ino_t parent)
769433d6423SLionel Sambuc {
770433d6423SLionel Sambuc   /* Read prototype lines and set up directory. Recurse if need be. */
771433d6423SLionel Sambuc   char *token[MAX_TOKENS], *p;
772433d6423SLionel Sambuc   char line[LINE_LEN];
773433d6423SLionel Sambuc   int mode, usrid, grpid, maj, min, f;
774433d6423SLionel Sambuc   ino_t n;
775433d6423SLionel Sambuc   zone_t z;
776433d6423SLionel Sambuc   size_t size;
777433d6423SLionel Sambuc 
778433d6423SLionel Sambuc   while (1) {
779433d6423SLionel Sambuc 	get_line(line, token);
780433d6423SLionel Sambuc 	p = token[0];
781433d6423SLionel Sambuc 	if (*p == '$') return;
782433d6423SLionel Sambuc 	p = token[1];
783433d6423SLionel Sambuc 	mode = mode_con(p);
784433d6423SLionel Sambuc 	usrid = atoi(token[2]);
785433d6423SLionel Sambuc 	grpid = atoi(token[3]);
786433d6423SLionel Sambuc 	n = alloc_inode(mode, usrid, grpid);
787433d6423SLionel Sambuc 
788433d6423SLionel Sambuc 	/* Enter name in directory and update directory's size. */
789433d6423SLionel Sambuc 	enter_dir(parent, token[0], n);
790433d6423SLionel Sambuc 	incr_size(parent, sizeof(struct direct));
791433d6423SLionel Sambuc 
792433d6423SLionel Sambuc 	/* Check to see if file is directory or special. */
793433d6423SLionel Sambuc 	incr_link(n);
794433d6423SLionel Sambuc 	if (*p == 'd') {
795433d6423SLionel Sambuc 		/* This is a directory. */
796433d6423SLionel Sambuc 		z = alloc_zone();	/* zone for new directory */
797433d6423SLionel Sambuc 		add_zone(n, z, 2 * sizeof(struct direct), current_time);
798433d6423SLionel Sambuc 		enter_dir(n, ".", n);
799433d6423SLionel Sambuc 		enter_dir(n, "..", parent);
800433d6423SLionel Sambuc 		incr_link(parent);
801433d6423SLionel Sambuc 		incr_link(n);
802433d6423SLionel Sambuc 		eat_dir(n);
803433d6423SLionel Sambuc 	} else if (*p == 'b' || *p == 'c') {
804433d6423SLionel Sambuc 		/* Special file. */
805433d6423SLionel Sambuc 		maj = atoi(token[4]);
806433d6423SLionel Sambuc 		min = atoi(token[5]);
807433d6423SLionel Sambuc 		size = 0;
808433d6423SLionel Sambuc 		if (token[6]) size = atoi(token[6]);
809433d6423SLionel Sambuc 		size = block_size * size;
810433d6423SLionel Sambuc 		add_zone(n, (zone_t) (makedev(maj,min)), size, current_time);
811433d6423SLionel Sambuc 	} else if (*p == 's') {
812433d6423SLionel Sambuc 		enter_symlink(n, token[4]);
813433d6423SLionel Sambuc 	} else {
814433d6423SLionel Sambuc 		/* Regular file. Go read it. */
815433d6423SLionel Sambuc 		if ((f = open(token[4], O_RDONLY)) < 0) {
816*cd34841dSBen Gras /* on minix natively, allow EACCES and skip the entry.
817*cd34841dSBen Gras  * while crossbuilding, always fail on error.
818*cd34841dSBen Gras  */
819*cd34841dSBen Gras #ifdef __minix
820*cd34841dSBen Gras 			if(errno == EACCES)
821*cd34841dSBen Gras 				warn("Can't open %s", token[4]);
822*cd34841dSBen Gras 			else
823*cd34841dSBen Gras #endif
824*cd34841dSBen Gras 				err(1, "Can't open %s", token[4]);
825433d6423SLionel Sambuc 		} else {
826433d6423SLionel Sambuc 			eat_file(n, f);
827433d6423SLionel Sambuc 		}
828433d6423SLionel Sambuc 	}
829433d6423SLionel Sambuc   }
830433d6423SLionel Sambuc 
831433d6423SLionel Sambuc }
832433d6423SLionel Sambuc 
833433d6423SLionel Sambuc /*================================================================
834433d6423SLionel Sambuc  * 		eat_file  -  copy file to MINIX
835433d6423SLionel Sambuc  *===============================================================*/
836433d6423SLionel Sambuc /* Zonesize >= blocksize */
837433d6423SLionel Sambuc void
838433d6423SLionel Sambuc eat_file(ino_t inode, int f)
839433d6423SLionel Sambuc {
840433d6423SLionel Sambuc   int ct = 0, i, j;
841433d6423SLionel Sambuc   zone_t z = 0;
842433d6423SLionel Sambuc   char *buf;
843433d6423SLionel Sambuc   time_t timeval;
844433d6423SLionel Sambuc 
845433d6423SLionel Sambuc   buf = alloc_block();
846433d6423SLionel Sambuc 
847433d6423SLionel Sambuc   do {
848433d6423SLionel Sambuc 	for (i = 0, j = 0; i < zone_per_block; i++, j += ct) {
849433d6423SLionel Sambuc 		memset(buf, 0, block_size);
850433d6423SLionel Sambuc 		if ((ct = read(f, buf, block_size)) > 0) {
851433d6423SLionel Sambuc 			if (i == 0) z = alloc_zone();
852433d6423SLionel Sambuc 			put_block((z << zone_shift) + i, buf);
853433d6423SLionel Sambuc 		}
854433d6423SLionel Sambuc 	}
855433d6423SLionel Sambuc 	timeval = (dflag ? current_time : file_time(f));
856433d6423SLionel Sambuc 	if (ct) add_zone(inode, z, (size_t) j, timeval);
857433d6423SLionel Sambuc   } while (ct == block_size);
858433d6423SLionel Sambuc   close(f);
859433d6423SLionel Sambuc   free(buf);
860433d6423SLionel Sambuc }
861433d6423SLionel Sambuc 
862433d6423SLionel Sambuc int
863433d6423SLionel Sambuc dir_try_enter(zone_t z, ino_t child, char const *name)
864433d6423SLionel Sambuc {
865433d6423SLionel Sambuc   struct direct *dir_entry = alloc_block();
866433d6423SLionel Sambuc   int r = 0;
867433d6423SLionel Sambuc   block_t b;
868433d6423SLionel Sambuc   int i, l;
869433d6423SLionel Sambuc 
870433d6423SLionel Sambuc   b = z << zone_shift;
871433d6423SLionel Sambuc   for (l = 0; l < zone_per_block; l++, b++) {
872433d6423SLionel Sambuc 	get_block(b, dir_entry);
873433d6423SLionel Sambuc 
874433d6423SLionel Sambuc 	for (i = 0; i < NR_DIR_ENTRIES(block_size); i++)
875433d6423SLionel Sambuc 		if (!dir_entry[i].d_ino)
876433d6423SLionel Sambuc 			break;
877433d6423SLionel Sambuc 
878433d6423SLionel Sambuc 	if(i < NR_DIR_ENTRIES(block_size)) {
879433d6423SLionel Sambuc 		r = 1;
880433d6423SLionel Sambuc 		dir_entry[i].d_ino = child;
881433d6423SLionel Sambuc 		assert(sizeof(dir_entry[i].d_name) == MFS_DIRSIZ);
882433d6423SLionel Sambuc 		if (verbose && strlen(name) > MFS_DIRSIZ)
883433d6423SLionel Sambuc 			fprintf(stderr, "File name %s is too long, truncated\n", name);
884433d6423SLionel Sambuc 		strncpy(dir_entry[i].d_name, name, MFS_DIRSIZ);
885433d6423SLionel Sambuc 		put_block(b, dir_entry);
886433d6423SLionel Sambuc 		break;
887433d6423SLionel Sambuc 	}
888433d6423SLionel Sambuc   }
889433d6423SLionel Sambuc 
890433d6423SLionel Sambuc   free(dir_entry);
891433d6423SLionel Sambuc 
892433d6423SLionel Sambuc   return r;
893433d6423SLionel Sambuc }
894433d6423SLionel Sambuc 
895433d6423SLionel Sambuc /*================================================================
896433d6423SLionel Sambuc  *	    directory & inode management assist group
897433d6423SLionel Sambuc  *===============================================================*/
898433d6423SLionel Sambuc void
899433d6423SLionel Sambuc enter_dir(ino_t parent, char const *name, ino_t child)
900433d6423SLionel Sambuc {
901433d6423SLionel Sambuc   /* Enter child in parent directory */
902433d6423SLionel Sambuc   /* Works for dir > 1 block and zone > block */
903433d6423SLionel Sambuc   unsigned int k;
904433d6423SLionel Sambuc   block_t b, indir;
905433d6423SLionel Sambuc   zone_t z;
906433d6423SLionel Sambuc   int off;
907433d6423SLionel Sambuc   struct inode *ino;
908433d6423SLionel Sambuc   struct inode *inoblock = alloc_block();
909433d6423SLionel Sambuc   zone_t *indirblock = alloc_block();
910433d6423SLionel Sambuc 
911433d6423SLionel Sambuc   assert(!(block_size % sizeof(struct direct)));
912433d6423SLionel Sambuc 
913433d6423SLionel Sambuc   /* Obtain the inode structure */
914433d6423SLionel Sambuc   b = ((parent - 1) / inodes_per_block) + inode_offset;
915433d6423SLionel Sambuc   off = (parent - 1) % inodes_per_block;
916433d6423SLionel Sambuc   get_block(b, inoblock);
917433d6423SLionel Sambuc   ino = inoblock + off;
918433d6423SLionel Sambuc 
919433d6423SLionel Sambuc   for (k = 0; k < NR_DZONES; k++) {
920433d6423SLionel Sambuc 	z = ino->i_zone[k];
921433d6423SLionel Sambuc 	if (z == 0) {
922433d6423SLionel Sambuc 		z = alloc_zone();
923433d6423SLionel Sambuc 		ino->i_zone[k] = z;
924433d6423SLionel Sambuc 	}
925433d6423SLionel Sambuc 
926433d6423SLionel Sambuc 	if(dir_try_enter(z, child, __UNCONST(name))) {
927433d6423SLionel Sambuc 		put_block(b, inoblock);
928433d6423SLionel Sambuc 		free(inoblock);
929433d6423SLionel Sambuc 		free(indirblock);
930433d6423SLionel Sambuc 		return;
931433d6423SLionel Sambuc 	}
932433d6423SLionel Sambuc   }
933433d6423SLionel Sambuc 
934433d6423SLionel Sambuc   /* no space in directory using just direct blocks; try indirect */
935433d6423SLionel Sambuc   if (ino->i_zone[S_INDIRECT_IDX] == 0)
936433d6423SLionel Sambuc   	ino->i_zone[S_INDIRECT_IDX] = alloc_zone();
937433d6423SLionel Sambuc 
938433d6423SLionel Sambuc   indir = ino->i_zone[S_INDIRECT_IDX] << zone_shift;
939433d6423SLionel Sambuc   --indir; /* Compensate for ++indir below */
940433d6423SLionel Sambuc   for(k = 0; k < (indir_per_zone); k++) {
941433d6423SLionel Sambuc 	if (k % indir_per_block == 0)
942433d6423SLionel Sambuc 		get_block(++indir, indirblock);
943433d6423SLionel Sambuc   	z = indirblock[k % indir_per_block];
944433d6423SLionel Sambuc 	if(!z) {
945433d6423SLionel Sambuc 		z = indirblock[k % indir_per_block] = alloc_zone();
946433d6423SLionel Sambuc 		put_block(indir, indirblock);
947433d6423SLionel Sambuc 	}
948433d6423SLionel Sambuc 	if(dir_try_enter(z, child, __UNCONST(name))) {
949433d6423SLionel Sambuc 		put_block(b, inoblock);
950433d6423SLionel Sambuc 		free(inoblock);
951433d6423SLionel Sambuc 		free(indirblock);
952433d6423SLionel Sambuc 		return;
953433d6423SLionel Sambuc 	}
954433d6423SLionel Sambuc   }
955433d6423SLionel Sambuc 
956433d6423SLionel Sambuc   pexit("Directory-inode %u beyond single indirect blocks.  Could not enter %s",
957433d6423SLionel Sambuc          (unsigned)parent, name);
958433d6423SLionel Sambuc }
959433d6423SLionel Sambuc 
960433d6423SLionel Sambuc 
961433d6423SLionel Sambuc void
962433d6423SLionel Sambuc add_zone(ino_t n, zone_t z, size_t bytes, time_t mtime)
963433d6423SLionel Sambuc {
964433d6423SLionel Sambuc   /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
965433d6423SLionel Sambuc 
966433d6423SLionel Sambuc   int off, i, j;
967433d6423SLionel Sambuc   block_t b;
968433d6423SLionel Sambuc   zone_t indir, dindir;
969433d6423SLionel Sambuc   struct inode *p, *inode;
970433d6423SLionel Sambuc   zone_t *blk, *dblk;
971433d6423SLionel Sambuc 
972433d6423SLionel Sambuc   assert(inodes_per_block*sizeof(*inode) == block_size);
973433d6423SLionel Sambuc   if(!(inode = alloc_block()))
974433d6423SLionel Sambuc   	err(1, "Couldn't allocate block of inodes");
975433d6423SLionel Sambuc 
976433d6423SLionel Sambuc   b = ((n - 1) / inodes_per_block) + inode_offset;
977433d6423SLionel Sambuc   off = (n - 1) % inodes_per_block;
978433d6423SLionel Sambuc   get_block(b, inode);
979433d6423SLionel Sambuc   p = &inode[off];
980433d6423SLionel Sambuc   p->i_size += bytes;
981433d6423SLionel Sambuc   p->i_mtime = mtime;
982433d6423SLionel Sambuc #ifndef MFS_INODE_ONLY_MTIME /* V1 file systems did not have them... */
983433d6423SLionel Sambuc   p->i_atime = p->i_ctime = current_time;
984433d6423SLionel Sambuc #endif
985433d6423SLionel Sambuc   for (i = 0; i < NR_DZONES; i++)
986433d6423SLionel Sambuc 	if (p->i_zone[i] == 0) {
987433d6423SLionel Sambuc 		p->i_zone[i] = z;
988433d6423SLionel Sambuc 		put_block(b, inode);
989433d6423SLionel Sambuc   		free(inode);
990433d6423SLionel Sambuc 		return;
991433d6423SLionel Sambuc 	}
992433d6423SLionel Sambuc 
993433d6423SLionel Sambuc   assert(indir_per_block*sizeof(*blk) == block_size);
994433d6423SLionel Sambuc   if(!(blk = alloc_block()))
995433d6423SLionel Sambuc   	err(1, "Couldn't allocate indirect block");
996433d6423SLionel Sambuc 
997433d6423SLionel Sambuc   /* File has grown beyond a small file. */
998433d6423SLionel Sambuc   if (p->i_zone[S_INDIRECT_IDX] == 0)
999433d6423SLionel Sambuc 	p->i_zone[S_INDIRECT_IDX] = alloc_zone();
1000433d6423SLionel Sambuc   indir = p->i_zone[S_INDIRECT_IDX] << zone_shift;
1001433d6423SLionel Sambuc   put_block(b, inode);
1002433d6423SLionel Sambuc   --indir; /* Compensate for ++indir below */
1003433d6423SLionel Sambuc   for (i = 0; i < (indir_per_zone); i++) {
1004433d6423SLionel Sambuc 	if (i % indir_per_block == 0)
1005433d6423SLionel Sambuc 		get_block(++indir, blk);
1006433d6423SLionel Sambuc 	if (blk[i % indir_per_block] == 0) {
1007433d6423SLionel Sambuc 		blk[i] = z;
1008433d6423SLionel Sambuc 		put_block(indir, blk);
1009433d6423SLionel Sambuc   		free(blk);
1010433d6423SLionel Sambuc   		free(inode);
1011433d6423SLionel Sambuc 		return;
1012433d6423SLionel Sambuc 	}
1013433d6423SLionel Sambuc   }
1014433d6423SLionel Sambuc 
1015433d6423SLionel Sambuc   /* File has grown beyond single indirect; we need a double indirect */
1016433d6423SLionel Sambuc   assert(indir_per_block*sizeof(*dblk) == block_size);
1017433d6423SLionel Sambuc   if(!(dblk = alloc_block()))
1018433d6423SLionel Sambuc   	err(1, "Couldn't allocate double indirect block");
1019433d6423SLionel Sambuc 
1020433d6423SLionel Sambuc   if (p->i_zone[D_INDIRECT_IDX] == 0)
1021433d6423SLionel Sambuc 	p->i_zone[D_INDIRECT_IDX] = alloc_zone();
1022433d6423SLionel Sambuc   dindir = p->i_zone[D_INDIRECT_IDX] << zone_shift;
1023433d6423SLionel Sambuc   put_block(b, inode);
1024433d6423SLionel Sambuc   --dindir; /* Compensate for ++indir below */
1025433d6423SLionel Sambuc   for (j = 0; j < (indir_per_zone); j++) {
1026433d6423SLionel Sambuc 	if (j % indir_per_block == 0)
1027433d6423SLionel Sambuc 		get_block(++dindir, dblk);
1028433d6423SLionel Sambuc 	if (dblk[j % indir_per_block] == 0)
1029433d6423SLionel Sambuc 		dblk[j % indir_per_block] = alloc_zone();
1030433d6423SLionel Sambuc 	indir = dblk[j % indir_per_block] << zone_shift;
1031433d6423SLionel Sambuc 	--indir; /* Compensate for ++indir below */
1032433d6423SLionel Sambuc 	for (i = 0; i < (indir_per_zone); i++) {
1033433d6423SLionel Sambuc 		if (i % indir_per_block == 0)
1034433d6423SLionel Sambuc 			get_block(++indir, blk);
1035433d6423SLionel Sambuc 		if (blk[i % indir_per_block] == 0) {
1036433d6423SLionel Sambuc 			blk[i] = z;
1037433d6423SLionel Sambuc 			put_block(dindir, dblk);
1038433d6423SLionel Sambuc 			put_block(indir, blk);
1039433d6423SLionel Sambuc 	  		free(dblk);
1040433d6423SLionel Sambuc 	  		free(blk);
1041433d6423SLionel Sambuc 	  		free(inode);
1042433d6423SLionel Sambuc 			return;
1043433d6423SLionel Sambuc 		}
1044433d6423SLionel Sambuc 	}
1045433d6423SLionel Sambuc   }
1046433d6423SLionel Sambuc 
1047433d6423SLionel Sambuc   pexit("File has grown beyond double indirect");
1048433d6423SLionel Sambuc }
1049433d6423SLionel Sambuc 
1050433d6423SLionel Sambuc 
1051433d6423SLionel Sambuc /* Increment the link count to inode n */
1052433d6423SLionel Sambuc void
1053433d6423SLionel Sambuc incr_link(ino_t n)
1054433d6423SLionel Sambuc {
1055433d6423SLionel Sambuc   int off;
1056433d6423SLionel Sambuc   static int enter = 0;
1057433d6423SLionel Sambuc   static struct inode *inodes = NULL;
1058433d6423SLionel Sambuc   block_t b;
1059433d6423SLionel Sambuc 
1060433d6423SLionel Sambuc   if (enter++) pexit("internal error: recursive call to incr_link()");
1061433d6423SLionel Sambuc 
1062433d6423SLionel Sambuc   b = ((n - 1) / inodes_per_block) + inode_offset;
1063433d6423SLionel Sambuc   off = (n - 1) % inodes_per_block;
1064433d6423SLionel Sambuc   {
1065433d6423SLionel Sambuc 	assert(sizeof(*inodes) * inodes_per_block == block_size);
1066433d6423SLionel Sambuc 	if(!inodes && !(inodes = alloc_block()))
1067433d6423SLionel Sambuc 		err(1, "couldn't allocate a block of inodes");
1068433d6423SLionel Sambuc 
1069433d6423SLionel Sambuc 	get_block(b, inodes);
1070433d6423SLionel Sambuc 	inodes[off].i_nlinks++;
1071433d6423SLionel Sambuc 	/* Check overflow (particularly on V1)... */
1072433d6423SLionel Sambuc 	if (inodes[off].i_nlinks <= 0)
1073433d6423SLionel Sambuc 		pexit("Too many links to a directory");
1074433d6423SLionel Sambuc 	put_block(b, inodes);
1075433d6423SLionel Sambuc   }
1076433d6423SLionel Sambuc   enter = 0;
1077433d6423SLionel Sambuc }
1078433d6423SLionel Sambuc 
1079433d6423SLionel Sambuc 
1080433d6423SLionel Sambuc /* Increment the file-size in inode n */
1081433d6423SLionel Sambuc void
1082433d6423SLionel Sambuc incr_size(ino_t n, size_t count)
1083433d6423SLionel Sambuc {
1084433d6423SLionel Sambuc   block_t b;
1085433d6423SLionel Sambuc   int off;
1086433d6423SLionel Sambuc 
1087433d6423SLionel Sambuc   b = ((n - 1) / inodes_per_block) + inode_offset;
1088433d6423SLionel Sambuc   off = (n - 1) % inodes_per_block;
1089433d6423SLionel Sambuc   {
1090433d6423SLionel Sambuc 	struct inode *inodes;
1091433d6423SLionel Sambuc 
1092433d6423SLionel Sambuc 	assert(inodes_per_block * sizeof(*inodes) == block_size);
1093433d6423SLionel Sambuc 	if(!(inodes = alloc_block()))
1094433d6423SLionel Sambuc 		err(1, "couldn't allocate a block of inodes");
1095433d6423SLionel Sambuc 
1096433d6423SLionel Sambuc 	get_block(b, inodes);
1097433d6423SLionel Sambuc 	/* Check overflow; avoid compiler spurious warnings */
1098433d6423SLionel Sambuc 	if (inodes[off].i_size+(int)count < inodes[off].i_size ||
1099433d6423SLionel Sambuc 	    inodes[off].i_size > MAX_MAX_SIZE-(int)count)
1100433d6423SLionel Sambuc 		pexit("File has become too big to be handled by MFS");
1101433d6423SLionel Sambuc 	inodes[off].i_size += count;
1102433d6423SLionel Sambuc 	put_block(b, inodes);
1103433d6423SLionel Sambuc 	free(inodes);
1104433d6423SLionel Sambuc   }
1105433d6423SLionel Sambuc }
1106433d6423SLionel Sambuc 
1107433d6423SLionel Sambuc 
1108433d6423SLionel Sambuc /*================================================================
1109433d6423SLionel Sambuc  * 	 	     allocation assist group
1110433d6423SLionel Sambuc  *===============================================================*/
1111433d6423SLionel Sambuc static ino_t
1112433d6423SLionel Sambuc alloc_inode(int mode, int usrid, int grpid)
1113433d6423SLionel Sambuc {
1114433d6423SLionel Sambuc   ino_t num;
1115433d6423SLionel Sambuc   int off;
1116433d6423SLionel Sambuc   block_t b;
1117433d6423SLionel Sambuc   struct inode *inodes;
1118433d6423SLionel Sambuc 
1119433d6423SLionel Sambuc   num = next_inode++;
1120433d6423SLionel Sambuc   if (num > nrinodes) {
1121*cd34841dSBen Gras   	pexit("File system does not have enough inodes (only %llu)", nrinodes);
1122433d6423SLionel Sambuc   }
1123433d6423SLionel Sambuc   b = ((num - 1) / inodes_per_block) + inode_offset;
1124433d6423SLionel Sambuc   off = (num - 1) % inodes_per_block;
1125433d6423SLionel Sambuc 
1126433d6423SLionel Sambuc   assert(inodes_per_block * sizeof(*inodes) == block_size);
1127433d6423SLionel Sambuc   if(!(inodes = alloc_block()))
1128433d6423SLionel Sambuc 	err(1, "couldn't allocate a block of inodes");
1129433d6423SLionel Sambuc 
1130433d6423SLionel Sambuc   get_block(b, inodes);
1131433d6423SLionel Sambuc   if (inodes[off].i_mode) {
1132*cd34841dSBen Gras 	pexit("allocation new inode %llu with non-zero mode - this cannot happen",
1133433d6423SLionel Sambuc 		num);
1134433d6423SLionel Sambuc   }
1135433d6423SLionel Sambuc   inodes[off].i_mode = mode;
1136433d6423SLionel Sambuc   inodes[off].i_uid = usrid;
1137433d6423SLionel Sambuc   inodes[off].i_gid = grpid;
1138433d6423SLionel Sambuc   if (verbose && (inodes[off].i_uid != usrid || inodes[off].i_gid != grpid))
1139433d6423SLionel Sambuc 	fprintf(stderr, "Uid/gid %d.%d do not fit within inode, truncated\n", usrid, grpid);
1140433d6423SLionel Sambuc   put_block(b, inodes);
1141433d6423SLionel Sambuc 
1142433d6423SLionel Sambuc   free(inodes);
1143433d6423SLionel Sambuc 
1144433d6423SLionel Sambuc   /* Set the bit in the bit map. */
1145433d6423SLionel Sambuc   insert_bit((block_t) INODE_MAP, num);
1146433d6423SLionel Sambuc   return(num);
1147433d6423SLionel Sambuc }
1148433d6423SLionel Sambuc 
1149433d6423SLionel Sambuc 
1150433d6423SLionel Sambuc /* Allocate a new zone */
1151433d6423SLionel Sambuc static zone_t
1152433d6423SLionel Sambuc alloc_zone(void)
1153433d6423SLionel Sambuc {
1154433d6423SLionel Sambuc   /* Works for zone > block */
1155433d6423SLionel Sambuc   block_t b;
1156433d6423SLionel Sambuc   int i;
1157433d6423SLionel Sambuc   zone_t z;
1158433d6423SLionel Sambuc 
1159433d6423SLionel Sambuc   z = next_zone++;
1160433d6423SLionel Sambuc   b = z << zone_shift;
1161433d6423SLionel Sambuc   if (b > nrblocks - zone_per_block)
1162433d6423SLionel Sambuc 	pexit("File system not big enough for all the files");
1163433d6423SLionel Sambuc   for (i = 0; i < zone_per_block; i++)
1164433d6423SLionel Sambuc 	put_block(b + i, zero);	/* give an empty zone */
1165433d6423SLionel Sambuc 
1166433d6423SLionel Sambuc   insert_bit(zone_map, z - zoff);
1167433d6423SLionel Sambuc   return z;
1168433d6423SLionel Sambuc }
1169433d6423SLionel Sambuc 
1170433d6423SLionel Sambuc 
1171433d6423SLionel Sambuc /* Insert one bit into the bitmap */
1172433d6423SLionel Sambuc void
1173433d6423SLionel Sambuc insert_bit(block_t map, bit_t bit)
1174433d6423SLionel Sambuc {
1175433d6423SLionel Sambuc   int boff, w, s;
1176433d6423SLionel Sambuc   unsigned int bits_per_block;
1177433d6423SLionel Sambuc   block_t map_block;
1178433d6423SLionel Sambuc   bitchunk_t *buf;
1179433d6423SLionel Sambuc 
1180433d6423SLionel Sambuc   buf = alloc_block();
1181433d6423SLionel Sambuc 
1182433d6423SLionel Sambuc   bits_per_block = FS_BITS_PER_BLOCK(block_size);
1183433d6423SLionel Sambuc   map_block = map + bit / bits_per_block;
1184433d6423SLionel Sambuc   if (map_block >= inode_offset)
1185433d6423SLionel Sambuc 	pexit("insertbit invades inodes area - this cannot happen");
1186433d6423SLionel Sambuc   boff = bit % bits_per_block;
1187433d6423SLionel Sambuc 
1188433d6423SLionel Sambuc   assert(boff >=0);
1189433d6423SLionel Sambuc   assert(boff < FS_BITS_PER_BLOCK(block_size));
1190433d6423SLionel Sambuc   get_block(map_block, buf);
1191433d6423SLionel Sambuc   w = boff / FS_BITCHUNK_BITS;
1192433d6423SLionel Sambuc   s = boff % FS_BITCHUNK_BITS;
1193433d6423SLionel Sambuc   buf[w] |= (1 << s);
1194433d6423SLionel Sambuc   put_block(map_block, buf);
1195433d6423SLionel Sambuc 
1196433d6423SLionel Sambuc   free(buf);
1197433d6423SLionel Sambuc }
1198433d6423SLionel Sambuc 
1199433d6423SLionel Sambuc 
1200433d6423SLionel Sambuc /*================================================================
1201433d6423SLionel Sambuc  * 		proto-file processing assist group
1202433d6423SLionel Sambuc  *===============================================================*/
1203433d6423SLionel Sambuc int mode_con(char *p)
1204433d6423SLionel Sambuc {
1205433d6423SLionel Sambuc   /* Convert string to mode */
1206433d6423SLionel Sambuc   int o1, o2, o3, mode;
1207433d6423SLionel Sambuc   char c1, c2, c3;
1208433d6423SLionel Sambuc 
1209433d6423SLionel Sambuc   c1 = *p++;
1210433d6423SLionel Sambuc   c2 = *p++;
1211433d6423SLionel Sambuc   c3 = *p++;
1212433d6423SLionel Sambuc   o1 = *p++ - '0';
1213433d6423SLionel Sambuc   o2 = *p++ - '0';
1214433d6423SLionel Sambuc   o3 = *p++ - '0';
1215433d6423SLionel Sambuc   mode = (o1 << 6) | (o2 << 3) | o3;
1216433d6423SLionel Sambuc   if (c1 == 'd') mode |= S_IFDIR;
1217433d6423SLionel Sambuc   if (c1 == 'b') mode |= S_IFBLK;
1218433d6423SLionel Sambuc   if (c1 == 'c') mode |= S_IFCHR;
1219433d6423SLionel Sambuc   if (c1 == 's') mode |= S_IFLNK;
1220433d6423SLionel Sambuc   if (c1 == 'l') mode |= S_IFLNK;	/* just to be somewhat ls-compatible*/
1221433d6423SLionel Sambuc /* XXX note: some other mkfs programs consider L to create hardlinks */
1222433d6423SLionel Sambuc   if (c1 == '-') mode |= S_IFREG;
1223433d6423SLionel Sambuc   if (c2 == 'u') mode |= S_ISUID;
1224433d6423SLionel Sambuc   if (c3 == 'g') mode |= S_ISGID;
1225433d6423SLionel Sambuc /* XXX There are no way to encode S_ISVTX */
1226433d6423SLionel Sambuc   return(mode);
1227433d6423SLionel Sambuc }
1228433d6423SLionel Sambuc 
1229433d6423SLionel Sambuc void
1230433d6423SLionel Sambuc get_line(char line[LINE_LEN], char *parse[MAX_TOKENS])
1231433d6423SLionel Sambuc {
1232433d6423SLionel Sambuc   /* Read a line and break it up in tokens */
1233433d6423SLionel Sambuc   int k;
1234433d6423SLionel Sambuc   char c, *p;
1235433d6423SLionel Sambuc   int d;
1236433d6423SLionel Sambuc 
1237433d6423SLionel Sambuc   for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0;
1238433d6423SLionel Sambuc   memset(line, 0, LINE_LEN);
1239433d6423SLionel Sambuc   k = 0;
1240433d6423SLionel Sambuc   p = line;
1241433d6423SLionel Sambuc   while (1) {
1242433d6423SLionel Sambuc 	if (++k > LINE_LEN) pexit("Line too long");
1243433d6423SLionel Sambuc 	d = fgetc(proto);
1244433d6423SLionel Sambuc 	if (d == EOF) pexit("Unexpected end-of-file");
1245433d6423SLionel Sambuc 	*p = d;
1246433d6423SLionel Sambuc 	if (*p == ' ' || *p == '\t') *p = 0;
1247433d6423SLionel Sambuc 	if (*p == '\n') {
1248433d6423SLionel Sambuc 		lct++;
1249433d6423SLionel Sambuc 		*p++ = 0;
1250433d6423SLionel Sambuc 		*p = '\n';
1251433d6423SLionel Sambuc 		break;
1252433d6423SLionel Sambuc 	}
1253433d6423SLionel Sambuc 	p++;
1254433d6423SLionel Sambuc   }
1255433d6423SLionel Sambuc 
1256433d6423SLionel Sambuc   k = 0;
1257433d6423SLionel Sambuc   p = line;
1258433d6423SLionel Sambuc   while (1) {
1259433d6423SLionel Sambuc 	c = *p++;
1260433d6423SLionel Sambuc 	if (c == '\n') return;
1261433d6423SLionel Sambuc 	if (c == 0) continue;
1262433d6423SLionel Sambuc 	parse[k++] = p - 1;
1263433d6423SLionel Sambuc 	do {
1264433d6423SLionel Sambuc 		c = *p++;
1265433d6423SLionel Sambuc 	} while (c != 0 && c != '\n');
1266433d6423SLionel Sambuc   }
1267433d6423SLionel Sambuc }
1268433d6423SLionel Sambuc 
1269433d6423SLionel Sambuc 
1270433d6423SLionel Sambuc /*================================================================
1271433d6423SLionel Sambuc  *			other stuff
1272433d6423SLionel Sambuc  *===============================================================*/
1273433d6423SLionel Sambuc 
1274433d6423SLionel Sambuc /*
1275433d6423SLionel Sambuc  * Check to see if the special file named 'device' is mounted.
1276433d6423SLionel Sambuc  */
1277433d6423SLionel Sambuc void
1278433d6423SLionel Sambuc check_mtab(const char *device)		/* /dev/hd1 or whatever */
1279433d6423SLionel Sambuc {
1280433d6423SLionel Sambuc #if defined(__minix)
1281433d6423SLionel Sambuc   int n, r;
1282433d6423SLionel Sambuc   struct stat sb;
1283433d6423SLionel Sambuc   char dev[PATH_MAX], mount_point[PATH_MAX],
1284433d6423SLionel Sambuc 	type[MNTNAMELEN], flags[MNTFLAGLEN];
1285433d6423SLionel Sambuc 
1286433d6423SLionel Sambuc   r= stat(device, &sb);
1287433d6423SLionel Sambuc   if (r == -1)
1288433d6423SLionel Sambuc   {
1289433d6423SLionel Sambuc 	if (errno == ENOENT)
1290433d6423SLionel Sambuc 		return;	/* Does not exist, and therefore not mounted. */
1291433d6423SLionel Sambuc 	err(1, "stat %s failed", device);
1292433d6423SLionel Sambuc   }
1293433d6423SLionel Sambuc   if (!S_ISBLK(sb.st_mode))
1294433d6423SLionel Sambuc   {
1295433d6423SLionel Sambuc 	/* Not a block device and therefore not mounted. */
1296433d6423SLionel Sambuc 	return;
1297433d6423SLionel Sambuc   }
1298433d6423SLionel Sambuc 
1299433d6423SLionel Sambuc   if (load_mtab(__UNCONST("mkfs")) < 0) return;
1300433d6423SLionel Sambuc   while (1) {
1301433d6423SLionel Sambuc 	n = get_mtab_entry(dev, mount_point, type, flags);
1302433d6423SLionel Sambuc 	if (n < 0) return;
1303433d6423SLionel Sambuc 	if (strcmp(device, dev) == 0) {
1304433d6423SLionel Sambuc 		/* Can't mkfs on top of a mounted file system. */
1305433d6423SLionel Sambuc 		errx(1, "%s is mounted on %s", device, mount_point);
1306433d6423SLionel Sambuc 	}
1307433d6423SLionel Sambuc   }
1308433d6423SLionel Sambuc #elif defined(__linux__)
1309433d6423SLionel Sambuc /* XXX: this code is copyright Theodore T'so and distributed under the GPLv2. Rewrite.
1310433d6423SLionel Sambuc  */
1311433d6423SLionel Sambuc 	struct mntent 	*mnt;
1312433d6423SLionel Sambuc 	struct stat	st_buf;
1313433d6423SLionel Sambuc 	dev_t		file_dev=0, file_rdev=0;
1314433d6423SLionel Sambuc 	ino_t		file_ino=0;
1315433d6423SLionel Sambuc 	FILE 		*f;
1316433d6423SLionel Sambuc 	int		fd;
1317433d6423SLionel Sambuc 	char 		*mtab_file = "/proc/mounts";
1318433d6423SLionel Sambuc 
1319433d6423SLionel Sambuc 	if ((f = setmntent (mtab_file, "r")) == NULL)
1320433d6423SLionel Sambuc 		goto error;
1321433d6423SLionel Sambuc 
1322433d6423SLionel Sambuc 	if (stat(device, &st_buf) == 0) {
1323433d6423SLionel Sambuc 		if (S_ISBLK(st_buf.st_mode)) {
1324433d6423SLionel Sambuc 			file_rdev = st_buf.st_rdev;
1325433d6423SLionel Sambuc 		} else {
1326433d6423SLionel Sambuc 			file_dev = st_buf.st_dev;
1327433d6423SLionel Sambuc 			file_ino = st_buf.st_ino;
1328433d6423SLionel Sambuc 		}
1329433d6423SLionel Sambuc 	}
1330433d6423SLionel Sambuc 
1331433d6423SLionel Sambuc 	while ((mnt = getmntent (f)) != NULL) {
1332433d6423SLionel Sambuc 		if (strcmp(device, mnt->mnt_fsname) == 0)
1333433d6423SLionel Sambuc 			break;
1334433d6423SLionel Sambuc 		if (stat(mnt->mnt_fsname, &st_buf) == 0) {
1335433d6423SLionel Sambuc 			if (S_ISBLK(st_buf.st_mode)) {
1336433d6423SLionel Sambuc 				if (file_rdev && (file_rdev == st_buf.st_rdev))
1337433d6423SLionel Sambuc 					break;
1338433d6423SLionel Sambuc 			} else {
1339433d6423SLionel Sambuc 				if (file_dev && ((file_dev == st_buf.st_dev) &&
1340433d6423SLionel Sambuc 						 (file_ino == st_buf.st_ino)))
1341433d6423SLionel Sambuc 					break;
1342433d6423SLionel Sambuc 			}
1343433d6423SLionel Sambuc 		}
1344433d6423SLionel Sambuc 	}
1345433d6423SLionel Sambuc 
1346433d6423SLionel Sambuc 	if (mnt == NULL) {
1347433d6423SLionel Sambuc 		/*
1348433d6423SLionel Sambuc 		 * Do an extra check to see if this is the root device.  We
1349433d6423SLionel Sambuc 		 * can't trust /etc/mtab, and /proc/mounts will only list
1350433d6423SLionel Sambuc 		 * /dev/root for the root filesystem.  Argh.  Instead we
1351433d6423SLionel Sambuc 		 * check if the given device has the same major/minor number
1352433d6423SLionel Sambuc 		 * as the device that the root directory is on.
1353433d6423SLionel Sambuc 		 */
1354433d6423SLionel Sambuc 		if (file_rdev && stat("/", &st_buf) == 0) {
1355433d6423SLionel Sambuc 			if (st_buf.st_dev == file_rdev) {
1356433d6423SLionel Sambuc 				goto is_root;
1357433d6423SLionel Sambuc 			}
1358433d6423SLionel Sambuc 		}
1359433d6423SLionel Sambuc 		goto test_busy;
1360433d6423SLionel Sambuc 	}
1361433d6423SLionel Sambuc 	/* Validate the entry in case /etc/mtab is out of date */
1362433d6423SLionel Sambuc 	/*
1363433d6423SLionel Sambuc 	 * We need to be paranoid, because some broken distributions
1364433d6423SLionel Sambuc 	 * (read: Slackware) don't initialize /etc/mtab before checking
1365433d6423SLionel Sambuc 	 * all of the non-root filesystems on the disk.
1366433d6423SLionel Sambuc 	 */
1367433d6423SLionel Sambuc 	if (stat(mnt->mnt_dir, &st_buf) < 0) {
1368433d6423SLionel Sambuc 		if (errno == ENOENT) {
1369433d6423SLionel Sambuc 			goto test_busy;
1370433d6423SLionel Sambuc 		}
1371433d6423SLionel Sambuc 		goto error;
1372433d6423SLionel Sambuc 	}
1373433d6423SLionel Sambuc 	if (file_rdev && (st_buf.st_dev != file_rdev)) {
1374433d6423SLionel Sambuc 		goto error;
1375433d6423SLionel Sambuc 	}
1376433d6423SLionel Sambuc 
1377433d6423SLionel Sambuc 	fprintf(stderr, "Device %s is mounted, exiting\n", device);
1378433d6423SLionel Sambuc 	exit(-1);
1379433d6423SLionel Sambuc 
1380433d6423SLionel Sambuc 	/*
1381433d6423SLionel Sambuc 	 * Check to see if we're referring to the root filesystem.
1382433d6423SLionel Sambuc 	 * If so, do a manual check to see if we can open /etc/mtab
1383433d6423SLionel Sambuc 	 * read/write, since if the root is mounted read/only, the
1384433d6423SLionel Sambuc 	 * contents of /etc/mtab may not be accurate.
1385433d6423SLionel Sambuc 	 */
1386433d6423SLionel Sambuc 	if (!strcmp(mnt->mnt_dir, "/")) {
1387433d6423SLionel Sambuc is_root:
1388433d6423SLionel Sambuc 		fprintf(stderr, "Device %s is mounted as root file system!\n",
1389433d6423SLionel Sambuc 				device);
1390433d6423SLionel Sambuc 		exit(-1);
1391433d6423SLionel Sambuc 	}
1392433d6423SLionel Sambuc 
1393433d6423SLionel Sambuc test_busy:
1394433d6423SLionel Sambuc 
1395433d6423SLionel Sambuc 	endmntent (f);
1396433d6423SLionel Sambuc 	if ((stat(device, &st_buf) != 0) ||
1397433d6423SLionel Sambuc 			!S_ISBLK(st_buf.st_mode))
1398433d6423SLionel Sambuc 		return;
1399433d6423SLionel Sambuc 	fd = open(device, O_RDONLY | O_EXCL);
1400433d6423SLionel Sambuc 	if (fd < 0) {
1401433d6423SLionel Sambuc 		if (errno == EBUSY) {
1402433d6423SLionel Sambuc 			fprintf(stderr, "Device %s is used by the system\n", device);
1403433d6423SLionel Sambuc 			exit(-1);
1404433d6423SLionel Sambuc 		}
1405433d6423SLionel Sambuc 	} else
1406433d6423SLionel Sambuc 		close(fd);
1407433d6423SLionel Sambuc 
1408433d6423SLionel Sambuc 	return;
1409433d6423SLionel Sambuc 
1410433d6423SLionel Sambuc error:
1411433d6423SLionel Sambuc 	endmntent (f);
1412433d6423SLionel Sambuc 	fprintf(stderr, "Error while checking if device %s is mounted\n", device);
1413433d6423SLionel Sambuc 	exit(-1);
1414433d6423SLionel Sambuc #else
1415433d6423SLionel Sambuc 	(void) device;	/* shut up warnings about unused variable... */
1416433d6423SLionel Sambuc #endif
1417433d6423SLionel Sambuc }
1418433d6423SLionel Sambuc 
1419433d6423SLionel Sambuc 
1420433d6423SLionel Sambuc time_t
1421433d6423SLionel Sambuc file_time(int f)
1422433d6423SLionel Sambuc {
1423433d6423SLionel Sambuc   struct stat statbuf;
1424433d6423SLionel Sambuc 
1425433d6423SLionel Sambuc   if (!fstat(f, &statbuf))
1426433d6423SLionel Sambuc 	return current_time;
1427433d6423SLionel Sambuc   if (statbuf.st_mtime<0 || statbuf.st_mtime>(uint32_t)(-1))
1428433d6423SLionel Sambuc 	return current_time;
1429433d6423SLionel Sambuc   return(statbuf.st_mtime);
1430433d6423SLionel Sambuc }
1431433d6423SLionel Sambuc 
1432433d6423SLionel Sambuc 
1433433d6423SLionel Sambuc __dead void
1434433d6423SLionel Sambuc pexit(char const * s, ...)
1435433d6423SLionel Sambuc {
1436433d6423SLionel Sambuc   va_list va;
1437433d6423SLionel Sambuc 
1438433d6423SLionel Sambuc   va_start(va, s);
1439433d6423SLionel Sambuc   vwarn(s, va);
1440433d6423SLionel Sambuc   va_end(va);
1441433d6423SLionel Sambuc   if (lct != 0)
1442433d6423SLionel Sambuc 	warnx("Line %d being processed when error detected.\n", lct);
1443433d6423SLionel Sambuc   exit(2);
1444433d6423SLionel Sambuc }
1445433d6423SLionel Sambuc 
1446433d6423SLionel Sambuc 
1447433d6423SLionel Sambuc void *
1448433d6423SLionel Sambuc alloc_block(void)
1449433d6423SLionel Sambuc {
1450433d6423SLionel Sambuc 	void *buf;
1451433d6423SLionel Sambuc 
1452433d6423SLionel Sambuc 	if(!(buf = malloc(block_size))) {
1453433d6423SLionel Sambuc 		err(1, "couldn't allocate filesystem buffer");
1454433d6423SLionel Sambuc 	}
1455433d6423SLionel Sambuc 	memset(buf, 0, block_size);
1456433d6423SLionel Sambuc 
1457433d6423SLionel Sambuc 	return buf;
1458433d6423SLionel Sambuc }
1459433d6423SLionel Sambuc 
1460433d6423SLionel Sambuc void
1461433d6423SLionel Sambuc print_fs(void)
1462433d6423SLionel Sambuc {
1463433d6423SLionel Sambuc   int i, j;
1464433d6423SLionel Sambuc   ino_t k;
1465433d6423SLionel Sambuc   struct inode *inode2;
1466433d6423SLionel Sambuc   unsigned short *usbuf;
1467433d6423SLionel Sambuc   block_t b;
1468433d6423SLionel Sambuc   struct direct *dir;
1469433d6423SLionel Sambuc 
1470433d6423SLionel Sambuc   assert(inodes_per_block * sizeof(*inode2) == block_size);
1471433d6423SLionel Sambuc   if(!(inode2 = alloc_block()))
1472433d6423SLionel Sambuc 	err(1, "couldn't allocate a block of inodes");
1473433d6423SLionel Sambuc 
1474433d6423SLionel Sambuc   assert(NR_DIR_ENTRIES(block_size)*sizeof(*dir) == block_size);
1475433d6423SLionel Sambuc   if(!(dir = alloc_block()))
1476433d6423SLionel Sambuc 	err(1, "couldn't allocate a block of directory entries");
1477433d6423SLionel Sambuc 
1478433d6423SLionel Sambuc   usbuf = alloc_block();
1479433d6423SLionel Sambuc   get_super_block(usbuf);
1480433d6423SLionel Sambuc   printf("\nSuperblock: ");
1481433d6423SLionel Sambuc   for (i = 0; i < 8; i++) printf("%06ho ", usbuf[i]);
1482433d6423SLionel Sambuc   printf("\n            ");
1483433d6423SLionel Sambuc   for (i = 0; i < 8; i++) printf("%#04hX ", usbuf[i]);
1484433d6423SLionel Sambuc   printf("\n            ");
1485433d6423SLionel Sambuc   for (i = 8; i < 15; i++) printf("%06ho ", usbuf[i]);
1486433d6423SLionel Sambuc   printf("\n            ");
1487433d6423SLionel Sambuc   for (i = 8; i < 15; i++) printf("%#04hX ", usbuf[i]);
1488433d6423SLionel Sambuc   get_block((block_t) INODE_MAP, usbuf);
1489433d6423SLionel Sambuc   printf("...\nInode map:  ");
1490433d6423SLionel Sambuc   for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]);
1491433d6423SLionel Sambuc   get_block((block_t) zone_map, usbuf);
1492433d6423SLionel Sambuc   printf("...\nZone  map:  ");
1493433d6423SLionel Sambuc   for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]);
1494433d6423SLionel Sambuc   printf("...\n");
1495433d6423SLionel Sambuc 
1496433d6423SLionel Sambuc   free(usbuf);
1497433d6423SLionel Sambuc   usbuf = NULL;
1498433d6423SLionel Sambuc 
1499433d6423SLionel Sambuc   k = 0;
1500433d6423SLionel Sambuc   for (b = inode_offset; k < nrinodes; b++) {
1501433d6423SLionel Sambuc 	get_block(b, inode2);
1502433d6423SLionel Sambuc 	for (i = 0; i < inodes_per_block; i++) {
1503433d6423SLionel Sambuc 		k = inodes_per_block * (int) (b - inode_offset) + i + 1;
1504433d6423SLionel Sambuc 		/* Lint but OK */
1505433d6423SLionel Sambuc 		if (k > nrinodes) break;
1506433d6423SLionel Sambuc 		{
1507433d6423SLionel Sambuc 			if (inode2[i].i_mode != 0) {
1508433d6423SLionel Sambuc 				printf("Inode %3u:  mode=", (unsigned)k);
1509433d6423SLionel Sambuc 				printf("%06o", (unsigned)inode2[i].i_mode);
1510433d6423SLionel Sambuc 				printf("  uid=%2d  gid=%2d  size=",
1511433d6423SLionel Sambuc 					(int)inode2[i].i_uid, (int)inode2[i].i_gid);
1512433d6423SLionel Sambuc 				printf("%6ld", (long)inode2[i].i_size);
1513433d6423SLionel Sambuc 				printf("  zone[0]=%u\n", (unsigned)inode2[i].i_zone[0]);
1514433d6423SLionel Sambuc 			}
1515433d6423SLionel Sambuc 			if ((inode2[i].i_mode & S_IFMT) == S_IFDIR) {
1516433d6423SLionel Sambuc 				/* This is a directory */
1517433d6423SLionel Sambuc 				get_block(inode2[i].i_zone[0] << zone_shift, dir);
1518433d6423SLionel Sambuc 				for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1519433d6423SLionel Sambuc 					if (dir[j].d_ino)
1520433d6423SLionel Sambuc 						printf("\tInode %2u: %s\n",
1521433d6423SLionel Sambuc 							(unsigned)dir[j].d_ino,
1522433d6423SLionel Sambuc 							dir[j].d_name);
1523433d6423SLionel Sambuc 			}
1524433d6423SLionel Sambuc 		}
1525433d6423SLionel Sambuc 	}
1526433d6423SLionel Sambuc   }
1527433d6423SLionel Sambuc 
1528433d6423SLionel Sambuc   if (zone_shift)
1529433d6423SLionel Sambuc 	printf("%d inodes used.     %u zones (%u blocks) used.\n",
1530433d6423SLionel Sambuc 		(int)next_inode-1, next_zone, next_zone*zone_per_block);
1531433d6423SLionel Sambuc   else
1532433d6423SLionel Sambuc 	printf("%d inodes used.     %u zones used.\n", (int)next_inode-1, next_zone);
1533433d6423SLionel Sambuc   free(dir);
1534433d6423SLionel Sambuc   free(inode2);
1535433d6423SLionel Sambuc }
1536433d6423SLionel Sambuc 
1537433d6423SLionel Sambuc 
1538433d6423SLionel Sambuc /*
1539433d6423SLionel Sambuc  * The first time a block is read, it returns all 0s, unless there has
1540433d6423SLionel Sambuc  * been a write.  This routine checks to see if a block has been accessed.
1541433d6423SLionel Sambuc  */
1542433d6423SLionel Sambuc int
1543433d6423SLionel Sambuc read_and_set(block_t n)
1544433d6423SLionel Sambuc {
1545433d6423SLionel Sambuc   int w, s, mask, r;
1546433d6423SLionel Sambuc 
1547433d6423SLionel Sambuc   w = n / 8;
1548433d6423SLionel Sambuc 
1549433d6423SLionel Sambuc   assert(n < nrblocks);
1550433d6423SLionel Sambuc   if(w >= umap_array_elements) {
1551433d6423SLionel Sambuc 	errx(1, "umap array too small - this can't happen");
1552433d6423SLionel Sambuc   }
1553433d6423SLionel Sambuc   s = n % 8;
1554433d6423SLionel Sambuc   mask = 1 << s;
1555433d6423SLionel Sambuc   r = (umap_array[w] & mask ? 1 : 0);
1556433d6423SLionel Sambuc   umap_array[w] |= mask;
1557433d6423SLionel Sambuc   return(r);
1558433d6423SLionel Sambuc }
1559433d6423SLionel Sambuc 
1560433d6423SLionel Sambuc __dead void
1561433d6423SLionel Sambuc usage(void)
1562433d6423SLionel Sambuc {
1563433d6423SLionel Sambuc   fprintf(stderr, "Usage: %s [-dltv] [-b blocks] [-i inodes]\n"
1564433d6423SLionel Sambuc 	"\t[-z zone_shift] [-I offset] [-x extra] [-B blocksize] special [proto]\n",
1565433d6423SLionel Sambuc       progname);
1566433d6423SLionel Sambuc   exit(4);
1567433d6423SLionel Sambuc }
1568433d6423SLionel Sambuc 
1569433d6423SLionel Sambuc void
1570433d6423SLionel Sambuc special(char * string, int insertmode)
1571433d6423SLionel Sambuc {
1572433d6423SLionel Sambuc   int openmode = O_RDWR;
1573433d6423SLionel Sambuc   if(!insertmode) openmode |= O_TRUNC;
1574433d6423SLionel Sambuc   fd = open(string, O_RDWR | O_CREAT, 0644);
1575433d6423SLionel Sambuc   if (fd < 0) err(1, "Can't open special file %s", string);
1576433d6423SLionel Sambuc   mkfs_seek(0, SEEK_SET);
1577433d6423SLionel Sambuc }
1578433d6423SLionel Sambuc 
1579433d6423SLionel Sambuc 
1580433d6423SLionel Sambuc 
1581433d6423SLionel Sambuc /* Read a block. */
1582433d6423SLionel Sambuc void
1583433d6423SLionel Sambuc get_block(block_t n, void *buf)
1584433d6423SLionel Sambuc {
1585433d6423SLionel Sambuc   ssize_t k;
1586433d6423SLionel Sambuc 
1587433d6423SLionel Sambuc   /* First access returns a zero block */
1588433d6423SLionel Sambuc   if (read_and_set(n) == 0) {
1589433d6423SLionel Sambuc 	memcpy(buf, zero, block_size);
1590433d6423SLionel Sambuc 	return;
1591433d6423SLionel Sambuc   }
1592433d6423SLionel Sambuc   mkfs_seek((uint64_t)(n) * block_size, SEEK_SET);
1593433d6423SLionel Sambuc   k = read(fd, buf, block_size);
1594433d6423SLionel Sambuc   if (k != block_size)
1595433d6423SLionel Sambuc 	pexit("get_block couldn't read block #%u", (unsigned)n);
1596433d6423SLionel Sambuc }
1597433d6423SLionel Sambuc 
1598433d6423SLionel Sambuc /* Read the super block. */
1599433d6423SLionel Sambuc void
1600433d6423SLionel Sambuc get_super_block(void *buf)
1601433d6423SLionel Sambuc {
1602433d6423SLionel Sambuc   ssize_t k;
1603433d6423SLionel Sambuc 
1604433d6423SLionel Sambuc   mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET);
1605433d6423SLionel Sambuc   k = read(fd, buf, SUPER_BLOCK_BYTES);
1606433d6423SLionel Sambuc   if (k != SUPER_BLOCK_BYTES)
1607433d6423SLionel Sambuc 	err(1, "get_super_block couldn't read super block");
1608433d6423SLionel Sambuc }
1609433d6423SLionel Sambuc 
1610433d6423SLionel Sambuc /* Write a block. */
1611433d6423SLionel Sambuc void
1612433d6423SLionel Sambuc put_block(block_t n, void *buf)
1613433d6423SLionel Sambuc {
1614433d6423SLionel Sambuc 
1615433d6423SLionel Sambuc   (void) read_and_set(n);
1616433d6423SLionel Sambuc 
1617433d6423SLionel Sambuc   mkfs_seek((uint64_t)(n) * block_size, SEEK_SET);
1618433d6423SLionel Sambuc   mkfs_write(buf, block_size);
1619433d6423SLionel Sambuc }
1620433d6423SLionel Sambuc 
1621433d6423SLionel Sambuc static ssize_t
1622433d6423SLionel Sambuc mkfs_write(void * buf, size_t count)
1623433d6423SLionel Sambuc {
1624433d6423SLionel Sambuc 	uint64_t fssize;
1625433d6423SLionel Sambuc 	ssize_t w;
1626433d6423SLionel Sambuc 
1627433d6423SLionel Sambuc 	/* Perform & check write */
1628433d6423SLionel Sambuc 	w = write(fd, buf, count);
1629433d6423SLionel Sambuc 	if(w < 0)
1630433d6423SLionel Sambuc 		err(1, "mkfs_write: write failed");
1631433d6423SLionel Sambuc 	if(w != count)
1632*cd34841dSBen Gras 		errx(1, "mkfs_write: short write: %zd != %zu", w, count);
1633433d6423SLionel Sambuc 
1634433d6423SLionel Sambuc 	/* Check if this has made the FS any bigger; count bytes after offset */
1635433d6423SLionel Sambuc 	fssize = mkfs_seek(0, SEEK_CUR);
1636433d6423SLionel Sambuc 
1637433d6423SLionel Sambuc 	assert(fssize >= fs_offset_bytes);
1638433d6423SLionel Sambuc 	fssize -= fs_offset_bytes;
1639433d6423SLionel Sambuc 	fssize = roundup(fssize, block_size);
1640433d6423SLionel Sambuc 	if(fssize > written_fs_size)
1641433d6423SLionel Sambuc 		written_fs_size = fssize;
1642433d6423SLionel Sambuc 
1643433d6423SLionel Sambuc 	return w;
1644433d6423SLionel Sambuc }
1645433d6423SLionel Sambuc 
1646433d6423SLionel Sambuc /* Seek to position in FS we're creating. */
1647433d6423SLionel Sambuc static uint64_t
1648433d6423SLionel Sambuc mkfs_seek(uint64_t pos, int whence)
1649433d6423SLionel Sambuc {
1650433d6423SLionel Sambuc 	if(whence == SEEK_SET) pos += fs_offset_bytes;
1651433d6423SLionel Sambuc 	off_t newpos;
1652433d6423SLionel Sambuc 	if((newpos=lseek(fd, pos, whence)) == (off_t) -1)
1653433d6423SLionel Sambuc 		err(1, "mkfs_seek: lseek failed");
1654433d6423SLionel Sambuc 	return newpos;
1655433d6423SLionel Sambuc }
1656