xref: /plan9/sys/lib/dist/cmd/bzfs/mkext.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 /*
2  * bzip2-based file system.
3  * the file system itself is just a bzipped2 xzipped mkfs archive
4  * prefixed with "bzfilesystem\n" and suffixed with
5  * a kilobyte of zeros.
6  *
7  * changes to the file system are only kept in
8  * memory, not written back to the disk.
9  *
10  * this is intended for use on a floppy boot disk.
11  * we assume the file is in the dos file system and
12  * contiguous on the disk: finding it amounts to
13  * looking at the beginning of each sector for
14  * "bzfilesystem\n".  then we pipe it through
15  * bunzip2 and store the files in a file tree in memory.
16  * things are slightly complicated by the fact that
17  * devfloppy requires reads to be on a 512-byte
18  * boundary and be a multiple of 512 bytes; we
19  * fork a process to relieve bunzip2 of this restriction.
20  */
21 
22 #include <u.h>
23 #include <libc.h>
24 #include <bio.h>
25 #include <auth.h>
26 #include <fcall.h>
27 #include "bzfs.h"
28 
29 enum{
30 	LEN	= 8*1024,
31 	NFLDS	= 6,		/* filename, modes, uid, gid, mtime, bytes */
32 };
33 
34 void	mkdirs(char*, char*);
35 void	mkdir(char*, ulong, ulong, char*, char*);
36 void	extract(char*, ulong, ulong, char*, char*, ulong);
37 void	seekpast(ulong);
38 void	error(char*, ...);
39 void	warn(char*, ...);
40 void	usage(void);
41 char *mtpt;
42 Biobufhdr bin;
43 uchar	binbuf[2*LEN];
44 
45 void
usage(void)46 usage(void)
47 {
48 	fprint(2, "usage: bzfs [-m mtpt] [-s] [-f file] [-h]\n");
49 	exits("usage");
50 }
51 
52 /*
53  * floppy disks can only be read on 512-byte
54  * boundaries and in 512 byte multiples.
55  * feed one over a pipe to allow arbitrary reading.
56  */
57 char zero[512];
58 int
blockread(int in,char * first,int nfirst)59 blockread(int in, char *first, int nfirst)
60 {
61 	int p[2], out, n, rv;
62 	char blk[512];
63 
64 	if(pipe(p) < 0)
65 		sysfatal("pipe: %r");
66 	rv = p[0];
67 	out = p[1];
68 	switch(rfork(RFPROC|RFNOTEG|RFFDG)){
69 	case -1:
70 		sysfatal("fork: %r");
71 	case 0:
72 		close(rv);
73 		break;
74 	default:
75 		close(in);
76 		close(out);
77 		return rv;
78 	}
79 
80 	write(out, first, nfirst);
81 
82 	while((n=read(in, blk, sizeof blk)) > 0){
83 		if(write(out, blk, n) != n)
84 			break;
85 		if(n == sizeof(blk) && memcmp(zero, blk, n) == n)
86 			break;
87 	}
88 	_exits(0);
89 	return -1;
90 }
91 
92 enum { NAMELEN = 28 };
93 
94 void
main(int argc,char ** argv)95 main(int argc, char **argv)
96 {
97 	char *rargv[10];
98 	int rargc;
99 	char *fields[NFLDS], name[2*LEN], *p, *namep;
100 	char uid[NAMELEN], gid[NAMELEN];
101 	ulong mode, bytes, mtime;
102 	char *file;
103 	int i, n, stdin, fd, chatty;
104 	char blk[512];
105 
106 	if(argc>1 && strcmp(argv[1], "RAMFS") == 0){
107 		argv[1] = argv[0];
108 		ramfsmain(argc-1, argv+1);
109 		exits(nil);
110 	}
111 	if(argc>1 && strcmp(argv[1], "BUNZIP") == 0){
112 		_unbzip(0, 1);
113 		exits(nil);
114 	}
115 
116 	rfork(RFNOTEG);
117 	stdin = 0;
118 	file = nil;
119 	namep = name;
120 	mtpt = "/root";
121 	chatty = 0;
122 	ARGBEGIN{
123 	case 'd':
124 		chatty = !chatty;
125 		break;
126 	case 'f':
127 		file = ARGF();
128 		break;
129 	case 's':
130 		stdin++;
131 		break;
132 	case 'm':
133 		mtpt = ARGF();
134 		break;
135 	default:
136 		usage();
137 	}ARGEND
138 
139 	if(argc != 0)
140 		usage();
141 
142 	if(file == nil) {
143 		fprint(2, "must specify -f file\n");
144 		usage();
145 	}
146 
147 	if((fd = open(file, OREAD)) < 0) {
148 		fprint(2, "cannot open \"%s\": %r\n", file);
149 		exits("open");
150 	}
151 
152 	rargv[0] = "ramfs";
153 	rargc = 1;
154 	if(stdin)
155 		rargv[rargc++] = "-i";
156 	rargv[rargc++] = "-m";
157 	rargv[rargc++] = mtpt;
158 	rargv[rargc] = nil;
159 	ramfsmain(rargc, rargv);
160 
161 	if(1 || strstr(file, "disk")) {	/* search for archive on block boundary */
162 if(chatty) fprint(2, "searching for bz\n");
163 		for(i=0;; i++){
164 			if((n = readn(fd, blk, sizeof blk)) != sizeof blk)
165 				sysfatal("read %d gets %d: %r\n", i, n);
166 			if(strncmp(blk, "bzfilesystem\n", 13) == 0)
167 				break;
168 		}
169 if(chatty) fprint(2, "found at %d\n", i);
170 	}
171 
172 	if(chdir(mtpt) < 0)
173 		error("chdir %s: %r", mtpt);
174 
175 	fd = unbflz(unbzip(blockread(fd, blk+13, sizeof(blk)-13)));
176 
177 	Binits(&bin, fd, OREAD, binbuf, sizeof binbuf);
178 	while(p = Brdline(&bin, '\n')){
179 		p[Blinelen(&bin)-1] = '\0';
180 if(chatty) fprint(2, "%s\n", p);
181 		if(strcmp(p, "end of archive") == 0){
182 			_exits(0);
183 		}
184 		if(getfields(p, fields, NFLDS, 0, " \t") != NFLDS){
185 			warn("too few fields in file header");
186 			continue;
187 		}
188 		strcpy(namep, fields[0]);
189 		mode = strtoul(fields[1], 0, 8);
190 		mtime = strtoul(fields[4], 0, 10);
191 		bytes = strtoul(fields[5], 0, 10);
192 		strncpy(uid, fields[2], NAMELEN);
193 		strncpy(gid, fields[3], NAMELEN);
194 		if(mode & DMDIR)
195 			mkdir(name, mode, mtime, uid, gid);
196 		else
197 			extract(name, mode, mtime, uid, gid, bytes);
198 	}
199 	fprint(2, "premature end of archive\n");
200 	exits("premature end of archive");
201 }
202 
203 char buf[8192];
204 
205 int
ffcreate(char * name,ulong mode,char * uid,char * gid,ulong mtime,int length)206 ffcreate(char *name, ulong mode, char *uid, char *gid, ulong mtime, int length)
207 {
208 	int fd, om;
209 	Dir nd;
210 
211 	sprint(buf, "%s/%s", mtpt, name);
212 	om = ORDWR;
213 	if(mode&DMDIR)
214 		om = OREAD;
215 	if((fd = create(buf, om, (mode&DMDIR)|0666)) < 0)
216 		error("create %s: %r", buf);
217 
218 	nulldir(&nd);
219 	nd.mode = mode;
220 	nd.uid = uid;
221 	nd.gid = gid;
222 	nd.mtime = mtime;
223 	if(length)
224 		nd.length = length;
225 	if(dirfwstat(fd, &nd) < 0)
226 		error("fwstat %s: %r", buf);
227 
228 	return fd;
229 }
230 
231 void
mkdir(char * name,ulong mode,ulong mtime,char * uid,char * gid)232 mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
233 {
234 	close(ffcreate(name, mode, uid, gid, mtime, 0));
235 }
236 
237 void
extract(char * name,ulong mode,ulong mtime,char * uid,char * gid,ulong bytes)238 extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, ulong bytes)
239 {
240 	int fd, tot, n;
241 
242 	fd = ffcreate(name, mode, uid, gid, mtime, bytes);
243 
244 	for(tot = 0; tot < bytes; tot += n){
245 		n = sizeof buf;
246 		if(tot + n > bytes)
247 			n = bytes - tot;
248 		n = Bread(&bin, buf, n);
249 		if(n <= 0)
250 			error("premature eof reading %s", name);
251 		if(write(fd, buf, n) != n)
252 			error("short write writing %s", name);
253 	}
254 	close(fd);
255 }
256 
257 void
error(char * fmt,...)258 error(char *fmt, ...)
259 {
260 	char buf[1024];
261 	va_list arg;
262 
263 	sprint(buf, "%s: ", argv0);
264 	va_start(arg, fmt);
265 	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
266 	va_end(arg);
267 	fprint(2, "%s\n", buf);
268 	exits(0);
269 }
270 
271 void
warn(char * fmt,...)272 warn(char *fmt, ...)
273 {
274 	char buf[1024];
275 	va_list arg;
276 
277 	sprint(buf, "%s: ", argv0);
278 	va_start(arg, fmt);
279 	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
280 	va_end(arg);
281 	fprint(2, "%s\n", buf);
282 }
283 
284 int
_efgfmt(Fmt *)285 _efgfmt(Fmt*)
286 {
287 	return -1;
288 }
289