• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* vi: set sw=8 ts=8: */
2 // genext2fs.c
3 //
4 // ext2 filesystem generator for embedded systems
5 // Copyright (C) 2000 Xavier Bestel <xavier.bestel@free.fr>
6 //
7 // Please direct support requests to genext2fs-devel@lists.sourceforge.net
8 //
9 // 'du' portions taken from coreutils/du.c in busybox:
10 //	Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
11 //	Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
12 //	Copyright (C) 2002  Edward Betts <edward@debian.org>
13 //
14 // This program is free software; you can redistribute it and/or
15 // modify it under the terms of the GNU General Public License
16 // as published by the Free Software Foundation; version
17 // 2 of the License.
18 //
19 // Changes:
20 // 	 3 Jun 2000	Initial release
21 // 	 6 Jun 2000	Bugfix: fs size multiple of 8
22 // 			Bugfix: fill blocks with inodes
23 // 	14 Jun 2000	Bugfix: bad chdir() with -d option
24 // 			Bugfix: removed size=8n constraint
25 // 			Changed -d file to -f file
26 // 			Added -e option
27 // 	22 Jun 2000	Changed types for 64bits archs
28 // 	24 Jun 2000	Added endianness swap
29 // 			Bugfix: bad dir name lookup
30 // 	03 Aug 2000	Bugfix: ind. blocks endian swap
31 // 	09 Aug 2000	Bugfix: symlinks endian swap
32 // 	01 Sep 2000	Bugfix: getopt returns int, not char	proski@gnu.org
33 // 	10 Sep 2000	Bugfix: device nodes endianness		xavier.gueguen@col.bsf.alcatel.fr
34 // 			Bugfix: getcwd values for Solaris	xavier.gueguen@col.bsf.alcatel.fr
35 // 			Bugfix: ANSI scanf for non-GNU C	xavier.gueguen@col.bsf.alcatel.fr
36 // 	28 Jun 2001	Bugfix: getcwd differs for Solaris/GNU	mike@sowbug.com
37 // 	 8 Mar 2002	Bugfix: endianness swap of x-indirects
38 // 	23 Mar 2002	Bugfix: test for IFCHR or IFBLK was flawed
39 // 	10 Oct 2002	Added comments,makefile targets,	vsundar@ixiacom.com
40 // 			endianess swap assert check.
41 // 			Copyright (C) 2002 Ixia communications
42 // 	12 Oct 2002	Added support for triple indirection	vsundar@ixiacom.com
43 // 			Copyright (C) 2002 Ixia communications
44 // 	14 Oct 2002	Added support for groups		vsundar@ixiacom.com
45 // 			Copyright (C) 2002 Ixia communications
46 // 	 5 Jan 2003	Bugfixes: reserved inodes should be set vsundar@usc.edu
47 // 			only in the first group; directory names
48 // 			need to be null padded at the end; and
49 // 			number of blocks per group should be a
50 // 			multiple of 8. Updated md5 values.
51 // 	 6 Jan 2003	Erik Andersen <andersee@debian.org> added
52 // 			mkfs.jffs2 compatible device table support,
53 // 			along with -q, -P, -U
54 
55 
56 #include <config.h>
57 #include <stdio.h>
58 
59 #if HAVE_SYS_TYPES_H
60 # include <sys/types.h>
61 #endif
62 
63 #if MAJOR_IN_MKDEV
64 # include <sys/mkdev.h>
65 #elif MAJOR_IN_SYSMACROS
66 # include <sys/sysmacros.h>
67 #endif
68 
69 #if HAVE_SYS_STAT_H
70 # include <sys/stat.h>
71 #endif
72 
73 #if STDC_HEADERS
74 # include <stdlib.h>
75 # include <stddef.h>
76 #else
77 # if HAVE_STDLIB_H
78 #  include <stdlib.h>
79 # endif
80 # if HAVE_STDDEF_H
81 #  include <stddef.h>
82 # endif
83 #endif
84 
85 #if HAVE_STRING_H
86 # if !STDC_HEADERS && HAVE_MEMORY_H
87 #  include <memory.h>
88 # endif
89 # include <string.h>
90 #endif
91 
92 #if HAVE_STRINGS_H
93 # include <strings.h>
94 #endif
95 
96 #if HAVE_INTTYPES_H
97 # include <inttypes.h>
98 #else
99 # if HAVE_STDINT_H
100 #  include <stdint.h>
101 # endif
102 #endif
103 
104 #if HAVE_UNISTD_H
105 # include <unistd.h>
106 #endif
107 
108 #if HAVE_DIRENT_H
109 # include <dirent.h>
110 # define NAMLEN(dirent) strlen((dirent)->d_name)
111 #else
112 # define dirent direct
113 # define NAMLEN(dirent) (dirent)->d_namlen
114 # if HAVE_SYS_NDIR_H
115 #  include <sys/ndir.h>
116 # endif
117 # if HAVE_SYS_DIR_H
118 #  include <sys/dir.h>
119 # endif
120 # if HAVE_NDIR_H
121 #  include <ndir.h>
122 # endif
123 #endif
124 
125 #if HAVE_LIBGEN_H
126 # include <libgen.h>
127 #endif
128 
129 #include <stdarg.h>
130 #include <assert.h>
131 #include <time.h>
132 #include <ctype.h>
133 #include <errno.h>
134 
135 #if HAVE_FCNTL_H
136 # include <fcntl.h>
137 #endif
138 
139 #if HAVE_GETOPT_H
140 # include <getopt.h>
141 #endif
142 
143 #if HAVE_LIMITS_H
144 # include <limits.h>
145 #endif
146 
147 #include <private/android_filesystem_config.h>
148 unsigned source_path_len = 0;
149 
150 struct stats {
151 	unsigned long nblocks;
152 	unsigned long ninodes;
153 };
154 
155 // block size
156 
157 #define BLOCKSIZE         1024
158 #define BLOCKS_PER_GROUP  8192
159 #define INODES_PER_GROUP  8192
160 /* Percentage of blocks that are reserved.*/
161 #define RESERVED_BLOCKS       5/100
162 #define MAX_RESERVED_BLOCKS  25/100
163 
164 
165 // inode block size (why is it != BLOCKSIZE ?!?)
166 /* The field i_blocks in the ext2 inode stores the number of data blocks
167    but in terms of 512 bytes. That is what INODE_BLOCKSIZE represents.
168    INOBLK is the number of such blocks in an actual disk block            */
169 
170 #define INODE_BLOCKSIZE   512
171 #define INOBLK            (BLOCKSIZE / INODE_BLOCKSIZE)
172 
173 // reserved inodes
174 
175 #define EXT2_BAD_INO         1     // Bad blocks inode
176 #define EXT2_ROOT_INO        2     // Root inode
177 #define EXT2_ACL_IDX_INO     3     // ACL inode
178 #define EXT2_ACL_DATA_INO    4     // ACL inode
179 #define EXT2_BOOT_LOADER_INO 5     // Boot loader inode
180 #define EXT2_UNDEL_DIR_INO   6     // Undelete directory inode
181 #define EXT2_FIRST_INO       11    // First non reserved inode
182 
183 // magic number for ext2
184 
185 #define EXT2_MAGIC_NUMBER  0xEF53
186 
187 
188 // direct/indirect block addresses
189 
190 #define EXT2_NDIR_BLOCKS   11                    // direct blocks
191 #define EXT2_IND_BLOCK     12                    // indirect block
192 #define EXT2_DIND_BLOCK    13                    // double indirect block
193 #define EXT2_TIND_BLOCK    14                    // triple indirect block
194 #define EXT2_INIT_BLOCK    0xFFFFFFFF            // just initialized (not really a block address)
195 
196 // end of a block walk
197 
198 #define WALK_END           0xFFFFFFFE
199 
200 // file modes
201 
202 #define FM_IFMT    0170000	// format mask
203 #define FM_IFSOCK  0140000	// socket
204 #define FM_IFLNK   0120000	// symbolic link
205 #define FM_IFREG   0100000	// regular file
206 
207 #define FM_IFBLK   0060000	// block device
208 #define FM_IFDIR   0040000	// directory
209 #define FM_IFCHR   0020000	// character device
210 #define FM_IFIFO   0010000	// fifo
211 
212 #define FM_IMASK   0007777	// *all* perms mask for everything below
213 
214 #define FM_ISUID   0004000	// SUID
215 #define FM_ISGID   0002000	// SGID
216 #define FM_ISVTX   0001000	// sticky bit
217 
218 #define FM_IRWXU   0000700	// entire "user" mask
219 #define FM_IRUSR   0000400	// read
220 #define FM_IWUSR   0000200	// write
221 #define FM_IXUSR   0000100	// execute
222 
223 #define FM_IRWXG   0000070	// entire "group" mask
224 #define FM_IRGRP   0000040	// read
225 #define FM_IWGRP   0000020	// write
226 #define FM_IXGRP   0000010	// execute
227 
228 #define FM_IRWXO   0000007	// entire "other" mask
229 #define FM_IROTH   0000004	// read
230 #define FM_IWOTH   0000002	// write
231 #define FM_IXOTH   0000001	// execute
232 
233 // options
234 
235 #define OP_HOLES     0x01       // make files with holes
236 
237 /* Defines for accessing group details */
238 
239 // Number of groups in the filesystem
240 #define GRP_NBGROUPS(fs) \
241 	(((fs)->sb.s_blocks_count - fs->sb.s_first_data_block + \
242 	  (fs)->sb.s_blocks_per_group - 1) / (fs)->sb.s_blocks_per_group)
243 
244 // Get group block bitmap (bbm) given the group number
245 #define GRP_GET_GROUP_BBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_block_bitmap) )
246 
247 // Get group inode bitmap (ibm) given the group number
248 #define GRP_GET_GROUP_IBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_inode_bitmap) )
249 
250 // Given an inode number find the group it belongs to
251 #define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb.s_inodes_per_group)
252 
253 //Given an inode number get the inode bitmap that covers it
254 #define GRP_GET_INODE_BITMAP(fs,nod) \
255 	( GRP_GET_GROUP_IBM((fs),GRP_GROUP_OF_INODE((fs),(nod))) )
256 
257 //Given an inode number find its offset within the inode bitmap that covers it
258 #define GRP_IBM_OFFSET(fs,nod) \
259 	( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb.s_inodes_per_group )
260 
261 // Given a block number find the group it belongs to
262 #define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb.s_blocks_per_group)
263 
264 //Given a block number get the block bitmap that covers it
265 #define GRP_GET_BLOCK_BITMAP(fs,blk) \
266 	( GRP_GET_GROUP_BBM((fs),GRP_GROUP_OF_BLOCK((fs),(blk))) )
267 
268 //Given a block number find its offset within the block bitmap that covers it
269 #define GRP_BBM_OFFSET(fs,blk) \
270 	( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb.s_blocks_per_group )
271 
272 
273 // used types
274 
275 typedef signed char int8;
276 typedef unsigned char uint8;
277 typedef signed short int16;
278 typedef unsigned short uint16;
279 typedef signed int int32;
280 typedef unsigned int uint32;
281 
282 
283 // the GNU C library has a wonderful scanf("%as", string) which will
284 // allocate the string with the right size, good to avoid buffer
285 // overruns. the following macros use it if available or use a
286 // hacky workaround
287 // moreover it will define a snprintf() like a sprintf(), i.e.
288 // without the buffer overrun checking, to work around bugs in
289 // older solaris. Note that this is still not very portable, in that
290 // the return value cannot be trusted.
291 
292 #if 0 // SCANF_CAN_MALLOC
293 // C99 define "a" for floating point, so you can have runtime surprise
294 // according the library versions
295 # define SCANF_PREFIX "a"
296 # define SCANF_STRING(s) (&s)
297 #else
298 # define SCANF_PREFIX "511"
299 # define SCANF_STRING(s) (s = malloc(512))
300 #endif /* SCANF_CAN_MALLOC */
301 
302 #if PREFER_PORTABLE_SNPRINTF
303 static inline int
portable_snprintf(char * str,size_t n,const char * fmt,...)304 portable_snprintf(char *str, size_t n, const char *fmt, ...)
305 {
306 	int ret;
307 	va_list ap;
308 	va_start(ap, fmt);
309 	ret = vsprintf(str, fmt, ap);
310 	va_end(ap);
311 	return ret;
312 }
313 # define SNPRINTF portable_snprintf
314 #else
315 # define SNPRINTF snprintf
316 #endif /* PREFER_PORTABLE_SNPRINTF */
317 
318 #if !HAVE_GETLINE
319 // getline() replacement for Darwin and Solaris etc.
320 // This code uses backward seeks (unless rchunk is set to 1) which can't work
321 // on pipes etc. However, add2fs_from_file() only calls getline() for
322 // regular files, so a larger rchunk and backward seeks are okay.
323 
324 ssize_t
getdelim(char ** lineptr,size_t * n,int delim,FILE * stream)325 getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
326 {
327 	char *p;                    // reads stored here
328 	size_t const rchunk = 512;  // number of bytes to read
329 	size_t const mchunk = 512;  // number of extra bytes to malloc
330 	size_t m = rchunk + 1;      // initial buffer size
331 
332 	if (*lineptr) {
333 		if (*n < m) {
334 			*lineptr = (char*)realloc(*lineptr, m);
335 			if (!*lineptr) return -1;
336 			*n = m;
337 		}
338 	} else {
339 		*lineptr = (char*)malloc(m);
340 		if (!*lineptr) return -1;
341 		*n = m;
342 	}
343 
344 	m = 0; // record length including seperator
345 
346 	do {
347 		size_t i;     // number of bytes read etc
348 		size_t j = 0; // number of bytes searched
349 
350 		p = *lineptr + m;
351 
352 		i = fread(p, 1, rchunk, stream);
353 		if (i < rchunk && ferror(stream))
354 			return -1;
355 		while (j < i) {
356 			++j;
357 			if (*p++ == (char)delim) {
358 				*p = '\0';
359 				if (j != i) {
360 					if (fseek(stream, j - i, SEEK_CUR))
361 						return -1;
362 					if (feof(stream))
363 						clearerr(stream);
364 				}
365 				m += j;
366 				return m;
367 			}
368 		}
369 
370 		m += j;
371 		if (feof(stream)) {
372 			if (m) return m;
373 			if (!i) return -1;
374 		}
375 
376 		// allocate space for next read plus possible null terminator
377 		i = ((m + (rchunk + 1 > mchunk ? rchunk + 1 : mchunk) +
378 		      mchunk - 1) / mchunk) * mchunk;
379 		if (i != *n) {
380 			*lineptr = (char*)realloc(*lineptr, i);
381 			if (!*lineptr) return -1;
382 			*n = i;
383 		}
384 	} while (1);
385 }
386 #define getline(a,b,c) getdelim(a,b,'\n',c)
387 #endif /* HAVE_GETLINE */
388 
389 // Convert a numerical string to a float, and multiply the result by an
390 // IEC or SI multiplier if provided; supported multipliers are Ki, Mi, Gi, k, M
391 // and G.
392 
393 float
SI_atof(const char * nptr)394 SI_atof(const char *nptr)
395 {
396 	float f = 0;
397 	float m = 1;
398 	char *suffixptr;
399 
400 #if HAVE_STRTOF
401 	f = strtof(nptr, &suffixptr);
402 #else
403 	f = (float)strtod(nptr, &suffixptr);
404 #endif /* HAVE_STRTOF */
405 
406 	if (*suffixptr) {
407 		if (!strcmp(suffixptr, "Ki"))
408 			m = 1 << 10;
409 		else if (!strcmp(suffixptr, "Mi"))
410 			m = 1 << 20;
411 		else if (!strcmp(suffixptr, "Gi"))
412 			m = 1 << 30;
413 		else if (!strcmp(suffixptr, "k"))
414 			m = 1000;
415 		else if (!strcmp(suffixptr, "M"))
416 			m = 1000 * 1000;
417 		else if (!strcmp(suffixptr, "G"))
418 			m = 1000 * 1000 * 1000;
419 	}
420 	return f * m;
421 }
422 
423 // endianness swap
424 
425 static inline uint16
swab16(uint16 val)426 swab16(uint16 val)
427 {
428 	return (val >> 8) | (val << 8);
429 }
430 
431 static inline uint32
swab32(uint32 val)432 swab32(uint32 val)
433 {
434 	return ((val>>24) | ((val>>8)&0xFF00) |
435 			((val<<8)&0xFF0000) | (val<<24));
436 }
437 
438 
439 // on-disk structures
440 // this trick makes me declare things only once
441 // (once for the structures, once for the endianness swap)
442 
443 #define superblock_decl \
444 	udecl32(s_inodes_count)        /* Count of inodes in the filesystem */ \
445 	udecl32(s_blocks_count)        /* Count of blocks in the filesystem */ \
446 	udecl32(s_r_blocks_count)      /* Count of the number of reserved blocks */ \
447 	udecl32(s_free_blocks_count)   /* Count of the number of free blocks */ \
448 	udecl32(s_free_inodes_count)   /* Count of the number of free inodes */ \
449 	udecl32(s_first_data_block)    /* The first block which contains data */ \
450 	udecl32(s_log_block_size)      /* Indicator of the block size */ \
451 	decl32(s_log_frag_size)        /* Indicator of the size of the fragments */ \
452 	udecl32(s_blocks_per_group)    /* Count of the number of blocks in each block group */ \
453 	udecl32(s_frags_per_group)     /* Count of the number of fragments in each block group */ \
454 	udecl32(s_inodes_per_group)    /* Count of the number of inodes in each block group */ \
455 	udecl32(s_mtime)               /* The time that the filesystem was last mounted */ \
456 	udecl32(s_wtime)               /* The time that the filesystem was last written to */ \
457 	udecl16(s_mnt_count)           /* The number of times the file system has been mounted */ \
458 	decl16(s_max_mnt_count)        /* The number of times the file system can be mounted */ \
459 	udecl16(s_magic)               /* Magic number indicating ex2fs */ \
460 	udecl16(s_state)               /* Flags indicating the current state of the filesystem */ \
461 	udecl16(s_errors)              /* Flags indicating the procedures for error reporting */ \
462 	udecl16(s_minor_rev_level)     /* The minor revision level of the filesystem */ \
463 	udecl32(s_lastcheck)           /* The time that the filesystem was last checked */ \
464 	udecl32(s_checkinterval)       /* The maximum time permissable between checks */ \
465 	udecl32(s_creator_os)          /* Indicator of which OS created the filesystem */ \
466 	udecl32(s_rev_level)           /* The revision level of the filesystem */ \
467 	udecl16(s_def_resuid)          /* The default uid for reserved blocks */ \
468 	udecl16(s_def_resgid)          /* The default gid for reserved blocks */
469 
470 #define groupdescriptor_decl \
471 	udecl32(bg_block_bitmap)       /* Block number of the block bitmap */ \
472 	udecl32(bg_inode_bitmap)       /* Block number of the inode bitmap */ \
473 	udecl32(bg_inode_table)        /* Block number of the inode table */ \
474 	udecl16(bg_free_blocks_count)  /* Free blocks in the group */ \
475 	udecl16(bg_free_inodes_count)  /* Free inodes in the group */ \
476 	udecl16(bg_used_dirs_count)    /* Number of directories in the group */ \
477 	udecl16(bg_pad)
478 
479 #define inode_decl \
480 	udecl16(i_mode)                /* Entry type and file mode */ \
481 	udecl16(i_uid)                 /* User id */ \
482 	udecl32(i_size)                /* File/dir size in bytes */ \
483 	udecl32(i_atime)               /* Last access time */ \
484 	udecl32(i_ctime)               /* Creation time */ \
485 	udecl32(i_mtime)               /* Last modification time */ \
486 	udecl32(i_dtime)               /* Deletion time */ \
487 	udecl16(i_gid)                 /* Group id */ \
488 	udecl16(i_links_count)         /* Number of (hard) links to this inode */ \
489 	udecl32(i_blocks)              /* Number of blocks used (1 block = 512 bytes) */ \
490 	udecl32(i_flags)               /* ??? */ \
491 	udecl32(i_reserved1) \
492 	utdecl32(i_block,15)           /* Blocks table */ \
493 	udecl32(i_version)             /* ??? */ \
494 	udecl32(i_file_acl)            /* File access control list */ \
495 	udecl32(i_dir_acl)             /* Directory access control list */ \
496 	udecl32(i_faddr)               /* Fragment address */ \
497 	udecl8(i_frag)                 /* Fragments count*/ \
498 	udecl8(i_fsize)                /* Fragment size */ \
499 	udecl16(i_pad1)
500 
501 #define directory_decl \
502 	udecl32(d_inode)               /* Inode entry */ \
503 	udecl16(d_rec_len)             /* Total size on record */ \
504 	udecl16(d_name_len)            /* Size of entry name */
505 
506 #define decl8(x) int8 x;
507 #define udecl8(x) uint8 x;
508 #define decl16(x) int16 x;
509 #define udecl16(x) uint16 x;
510 #define decl32(x) int32 x;
511 #define udecl32(x) uint32 x;
512 #define utdecl32(x,n) uint32 x[n];
513 
514 typedef struct
515 {
516 	superblock_decl
517 	uint32 s_reserved[235];       // Reserved
518 } superblock;
519 
520 typedef struct
521 {
522 	groupdescriptor_decl
523 	uint32 bg_reserved[3];
524 } groupdescriptor;
525 
526 typedef struct
527 {
528 	inode_decl
529 	uint32 i_reserved2[2];
530 } inode;
531 
532 typedef struct
533 {
534 	directory_decl
535 	char d_name[0];
536 } directory;
537 
538 typedef uint8 block[BLOCKSIZE];
539 
540 /* blockwalker fields:
541    The blockwalker is used to access all the blocks of a file (including
542    the indirection blocks) through repeated calls to walk_bw.
543 
544    bpdir -> index into the inode->i_block[]. Indicates level of indirection.
545    bnum -> total number of blocks so far accessed. including indirection
546            blocks.
547    bpind,bpdind,bptind -> index into indirection blocks.
548 
549    bpind, bpdind, bptind do *NOT* index into single, double and triple
550    indirect blocks resp. as you might expect from their names. Instead
551    they are in order the 1st, 2nd & 3rd index to be used
552 
553    As an example..
554    To access data block number 70000:
555         bpdir: 15 (we are doing triple indirection)
556         bpind: 0 ( index into the triple indirection block)
557         bpdind: 16 ( index into the double indirection block)
558         bptind: 99 ( index into the single indirection block)
559 	70000 = 12 + 256 + 256*256 + 16*256 + 100 (indexing starts from zero)
560 
561    So,for double indirection bpind will index into the double indirection
562    block and bpdind into the single indirection block. For single indirection
563    only bpind will be used.
564 */
565 
566 typedef struct
567 {
568 	uint32 bnum;
569 	uint32 bpdir;
570 	uint32 bpind;
571 	uint32 bpdind;
572 	uint32 bptind;
573 } blockwalker;
574 
575 
576 /* Filesystem structure that support groups */
577 #if BLOCKSIZE == 1024
578 typedef struct
579 {
580 	block zero;            // The famous block 0
581 	superblock sb;         // The superblock
582 	groupdescriptor gd[0]; // The group descriptors
583 } filesystem;
584 #else
585 #error UNHANDLED BLOCKSIZE
586 #endif
587 
588 // now the endianness swap
589 
590 #undef decl8
591 #undef udecl8
592 #undef decl16
593 #undef udecl16
594 #undef decl32
595 #undef udecl32
596 #undef utdecl32
597 
598 #define decl8(x)
599 #define udecl8(x)
600 #define decl16(x) this->x = swab16(this->x);
601 #define udecl16(x) this->x = swab16(this->x);
602 #define decl32(x) this->x = swab32(this->x);
603 #define udecl32(x) this->x = swab32(this->x);
604 #define utdecl32(x,n) { int i; for(i=0; i<n; i++) this->x[i] = swab32(this->x[i]); }
605 
606 #define HDLINK_CNT   16
607 static int32 hdlink_cnt = HDLINK_CNT;
608 struct hdlink_s
609 {
610 	uint32	src_inode;
611 	uint32	dst_nod;
612 };
613 
614 struct hdlinks_s
615 {
616 	int32 count;
617 	struct hdlink_s *hdl;
618 };
619 
620 static struct hdlinks_s hdlinks;
621 
622 static void
swap_sb(superblock * sb)623 swap_sb(superblock *sb)
624 {
625 #define this sb
626 	superblock_decl
627 #undef this
628 }
629 
630 static void
swap_gd(groupdescriptor * gd)631 swap_gd(groupdescriptor *gd)
632 {
633 #define this gd
634 	groupdescriptor_decl
635 #undef this
636 }
637 
638 static void
swap_nod(inode * nod)639 swap_nod(inode *nod)
640 {
641 #define this nod
642 	inode_decl
643 #undef this
644 }
645 
646 static void
swap_dir(directory * dir)647 swap_dir(directory *dir)
648 {
649 #define this dir
650 	directory_decl
651 #undef this
652 }
653 
654 static void
swap_block(block b)655 swap_block(block b)
656 {
657 	int i;
658 	uint32 *blk = (uint32*)b;
659 	for(i = 0; i < BLOCKSIZE/4; i++)
660 		blk[i] = swab32(blk[i]);
661 }
662 
663 #undef decl8
664 #undef udecl8
665 #undef decl16
666 #undef udecl16
667 #undef decl32
668 #undef udecl32
669 #undef utdecl32
670 
671 static char * app_name;
672 static const char *const memory_exhausted = "memory exhausted";
673 
674 // error (un)handling
675 static void
verror_msg(const char * s,va_list p)676 verror_msg(const char *s, va_list p)
677 {
678 	fflush(stdout);
679 	fprintf(stderr, "%s: ", app_name);
680 	vfprintf(stderr, s, p);
681 }
682 static void
error_msg(const char * s,...)683 error_msg(const char *s, ...)
684 {
685 	va_list p;
686 	va_start(p, s);
687 	verror_msg(s, p);
688 	va_end(p);
689 	putc('\n', stderr);
690 }
691 
692 static void
error_msg_and_die(const char * s,...)693 error_msg_and_die(const char *s, ...)
694 {
695 	va_list p;
696 	va_start(p, s);
697 	verror_msg(s, p);
698 	va_end(p);
699 	putc('\n', stderr);
700 	exit(EXIT_FAILURE);
701 }
702 
703 static void
vperror_msg(const char * s,va_list p)704 vperror_msg(const char *s, va_list p)
705 {
706 	int err = errno;
707 	if (s == 0)
708 		s = "";
709 	verror_msg(s, p);
710 	if (*s)
711 		s = ": ";
712 	fprintf(stderr, "%s%s\n", s, strerror(err));
713 }
714 
715 static void
perror_msg_and_die(const char * s,...)716 perror_msg_and_die(const char *s, ...)
717 {
718 	va_list p;
719 	va_start(p, s);
720 	vperror_msg(s, p);
721 	va_end(p);
722 	exit(EXIT_FAILURE);
723 }
724 
725 static FILE *
xfopen(const char * path,const char * mode)726 xfopen(const char *path, const char *mode)
727 {
728 	FILE *fp;
729 	if ((fp = fopen(path, mode)) == NULL)
730 		perror_msg_and_die("%s", path);
731 	return fp;
732 }
733 
734 static char *
xstrdup(const char * s)735 xstrdup(const char *s)
736 {
737 	char *t;
738 
739 	if (s == NULL)
740 		return NULL;
741 	t = strdup(s);
742 	if (t == NULL)
743 		error_msg_and_die(memory_exhausted);
744 	return t;
745 }
746 
747 static void *
xrealloc(void * ptr,size_t size)748 xrealloc(void *ptr, size_t size)
749 {
750 	ptr = realloc(ptr, size);
751 	if (ptr == NULL && size != 0)
752 		error_msg_and_die(memory_exhausted);
753 	return ptr;
754 }
755 
756 static char *
xreadlink(const char * path)757 xreadlink(const char *path)
758 {
759 	static const int GROWBY = 80; /* how large we will grow strings by */
760 
761 	char *buf = NULL;
762 	int bufsize = 0, readsize = 0;
763 
764 	do {
765 		buf = xrealloc(buf, bufsize += GROWBY);
766 		readsize = readlink(path, buf, bufsize); /* 1st try */
767 		if (readsize == -1) {
768 			perror_msg_and_die("%s:%s", app_name, path);
769 		}
770 	}
771 	while (bufsize < readsize + 1);
772 
773 	buf[readsize] = '\0';
774 	return buf;
775 }
776 
777 int
is_hardlink(ino_t inode)778 is_hardlink(ino_t inode)
779 {
780 	int i;
781 
782 	for(i = 0; i < hdlinks.count; i++) {
783 		if(hdlinks.hdl[i].src_inode == inode)
784 			return i;
785 	}
786 	return -1;
787 }
788 
789 // printf helper macro
790 #define plural(a) (a), ((a) > 1) ? "s" : ""
791 
792 // temporary working block
793 static inline uint8 *
get_workblk(void)794 get_workblk(void)
795 {
796 	unsigned char* b=calloc(1,BLOCKSIZE);
797 	return b;
798 }
799 static inline void
free_workblk(block b)800 free_workblk(block b)
801 {
802 	free(b);
803 }
804 
805 /* Rounds qty upto a multiple of siz. siz should be a power of 2 */
806 static inline uint32
rndup(uint32 qty,uint32 siz)807 rndup(uint32 qty, uint32 siz)
808 {
809 	return (qty + (siz - 1)) & ~(siz - 1);
810 }
811 
812 // check if something is allocated in the bitmap
813 static inline uint32
allocated(block b,uint32 item)814 allocated(block b, uint32 item)
815 {
816 	return b[(item-1) / 8] & (1 << ((item-1) % 8));
817 }
818 
819 // return a given block from a filesystem
820 static inline uint8 *
get_blk(filesystem * fs,uint32 blk)821 get_blk(filesystem *fs, uint32 blk)
822 {
823 	return (uint8*)fs + blk*BLOCKSIZE;
824 }
825 
826 // return a given inode from a filesystem
827 static inline inode *
get_nod(filesystem * fs,uint32 nod)828 get_nod(filesystem *fs, uint32 nod)
829 {
830 	int grp,offset;
831 	inode *itab;
832 
833 	offset = GRP_IBM_OFFSET(fs,nod);
834 	grp = GRP_GROUP_OF_INODE(fs,nod);
835 	itab = (inode *)get_blk(fs, fs->gd[grp].bg_inode_table);
836 	return itab+offset-1;
837 }
838 
839 // allocate a given block/inode in the bitmap
840 // allocate first free if item == 0
841 static uint32
allocate(block b,uint32 item)842 allocate(block b, uint32 item)
843 {
844 	if(!item)
845 	{
846 		int i;
847 		uint8 bits;
848 		for(i = 0; i < BLOCKSIZE; i++)
849 			if((bits = b[i]) != (uint8)-1)
850 			{
851 				int j;
852 				for(j = 0; j < 8; j++)
853 					if(!(bits & (1 << j)))
854 						break;
855 				item = i * 8 + j + 1;
856 				break;
857 			}
858 		if(i == BLOCKSIZE)
859 			return 0;
860 	}
861 	b[(item-1) / 8] |= (1 << ((item-1) % 8));
862 	return item;
863 }
864 
865 // deallocate a given block/inode
866 static void
deallocate(block b,uint32 item)867 deallocate(block b, uint32 item)
868 {
869 	b[(item-1) / 8] &= ~(1 << ((item-1) % 8));
870 }
871 
872 // allocate a block
873 static uint32
alloc_blk(filesystem * fs,uint32 nod)874 alloc_blk(filesystem *fs, uint32 nod)
875 {
876 	uint32 bk=0;
877 	uint32 grp,nbgroups;
878 
879 	grp = GRP_GROUP_OF_INODE(fs,nod);
880 	nbgroups = GRP_NBGROUPS(fs);
881 	if(!(bk = allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), 0))) {
882 		for(grp=0;grp<nbgroups && !bk;grp++)
883 			bk=allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap),0);
884 		grp--;
885 	}
886 	if (!bk)
887 		error_msg_and_die("couldn't allocate a block (no free space)");
888 	if(!(fs->gd[grp].bg_free_blocks_count--))
889 		error_msg_and_die("group descr %d. free blocks count == 0 (corrupted fs?)",grp);
890 	if(!(fs->sb.s_free_blocks_count--))
891 		error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
892 	return fs->sb.s_blocks_per_group*grp + bk;
893 }
894 
895 // free a block
896 static void
free_blk(filesystem * fs,uint32 bk)897 free_blk(filesystem *fs, uint32 bk)
898 {
899 	uint32 grp;
900 
901 	grp = bk / fs->sb.s_blocks_per_group;
902 	bk %= fs->sb.s_blocks_per_group;
903 	deallocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), bk);
904 	fs->gd[grp].bg_free_blocks_count++;
905 	fs->sb.s_free_blocks_count++;
906 }
907 
908 // allocate an inode
909 static uint32
alloc_nod(filesystem * fs)910 alloc_nod(filesystem *fs)
911 {
912 	uint32 nod,best_group=0;
913 	uint32 grp,nbgroups,avefreei;
914 
915 	nbgroups = GRP_NBGROUPS(fs);
916 
917 	/* Distribute inodes amongst all the blocks                           */
918 	/* For every block group with more than average number of free inodes */
919 	/* find the one with the most free blocks and allocate node there     */
920 	/* Idea from find_group_dir in fs/ext2/ialloc.c in 2.4.19 kernel      */
921 	/* We do it for all inodes.                                           */
922 	avefreei  =  fs->sb.s_free_inodes_count / nbgroups;
923 	for(grp=0; grp<nbgroups; grp++) {
924 		if (fs->gd[grp].bg_free_inodes_count < avefreei ||
925 		    fs->gd[grp].bg_free_inodes_count == 0)
926 			continue;
927 		if (!best_group ||
928 			fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count)
929 			best_group = grp;
930 	}
931 	if (!(nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap),0)))
932 		error_msg_and_die("couldn't allocate an inode (no free inode)");
933 	if(!(fs->gd[best_group].bg_free_inodes_count--))
934 		error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)");
935 	if(!(fs->sb.s_free_inodes_count--))
936 		error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
937 	return fs->sb.s_inodes_per_group*best_group+nod;
938 }
939 
940 // print a bitmap allocation
941 static void
print_bm(block b,uint32 max)942 print_bm(block b, uint32 max)
943 {
944 	uint32 i;
945 	printf("----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0\n");
946 	for(i=1; i <= max; i++)
947 	{
948 		putchar(allocated(b, i) ? '*' : '.');
949 		if(!(i % 100))
950 			printf("\n");
951 	}
952 	if((i-1) % 100)
953 		printf("\n");
954 }
955 
956 // initalize a blockwalker (iterator for blocks list)
957 static inline void
init_bw(blockwalker * bw)958 init_bw(blockwalker *bw)
959 {
960 	bw->bnum = 0;
961 	bw->bpdir = EXT2_INIT_BLOCK;
962 }
963 
964 // return next block of inode (WALK_END for end)
965 // if *create>0, append a newly allocated block at the end
966 // if *create<0, free the block - warning, the metadata blocks contents is
967 //				  used after being freed, so once you start
968 //				  freeing blocks don't stop until the end of
969 //				  the file. moreover, i_blocks isn't updated.
970 //				  in fact, don't do that, just use extend_blk
971 // if hole!=0, create a hole in the file
972 static uint32
walk_bw(filesystem * fs,uint32 nod,blockwalker * bw,int32 * create,uint32 hole)973 walk_bw(filesystem *fs, uint32 nod, blockwalker *bw, int32 *create, uint32 hole)
974 {
975 	uint32 *bkref = 0;
976 	uint32 *b;
977 	int extend = 0, reduce = 0;
978 	if(create && (*create) < 0)
979 		reduce = 1;
980 	if(bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK)
981 	{
982 		if(create && (*create) > 0)
983 		{
984 			(*create)--;
985 			extend = 1;
986 		}
987 		else
988 			return WALK_END;
989 	}
990 	// first direct block
991 	if(bw->bpdir == EXT2_INIT_BLOCK)
992 	{
993 		bkref = &get_nod(fs, nod)->i_block[bw->bpdir = 0];
994 		if(extend) // allocate first block
995 			*bkref = hole ? 0 : alloc_blk(fs,nod);
996 		if(reduce) // free first block
997 			free_blk(fs, *bkref);
998 	}
999 	// direct block
1000 	else if(bw->bpdir < EXT2_NDIR_BLOCKS)
1001 	{
1002 		bkref = &get_nod(fs, nod)->i_block[++bw->bpdir];
1003 		if(extend) // allocate block
1004 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1005 		if(reduce) // free block
1006 			free_blk(fs, *bkref);
1007 	}
1008 	// first block in indirect block
1009 	else if(bw->bpdir == EXT2_NDIR_BLOCKS)
1010 	{
1011 		bw->bnum++;
1012 		bw->bpdir = EXT2_IND_BLOCK;
1013 		bw->bpind = 0;
1014 		if(extend) // allocate indirect block
1015 			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
1016 		if(reduce) // free indirect block
1017 			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1018 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1019 		bkref = &b[bw->bpind];
1020 		if(extend) // allocate first block
1021 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1022 		if(reduce) // free first block
1023 			free_blk(fs, *bkref);
1024 	}
1025 	// block in indirect block
1026 	else if((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
1027 	{
1028 		bw->bpind++;
1029 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1030 		bkref = &b[bw->bpind];
1031 		if(extend) // allocate block
1032 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1033 		if(reduce) // free block
1034 			free_blk(fs, *bkref);
1035 	}
1036 	// first block in first indirect block in first double indirect block
1037 	else if(bw->bpdir == EXT2_IND_BLOCK)
1038 	{
1039 		bw->bnum += 2;
1040 		bw->bpdir = EXT2_DIND_BLOCK;
1041 		bw->bpind = 0;
1042 		bw->bpdind = 0;
1043 		if(extend) // allocate double indirect block
1044 			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
1045 		if(reduce) // free double indirect block
1046 			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1047 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1048 		if(extend) // allocate first indirect block
1049 			b[bw->bpind] = alloc_blk(fs,nod);
1050 		if(reduce) // free  firstindirect block
1051 			free_blk(fs, b[bw->bpind]);
1052 		b = (uint32*)get_blk(fs, b[bw->bpind]);
1053 		bkref = &b[bw->bpdind];
1054 		if(extend) // allocate first block
1055 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1056 		if(reduce) // free first block
1057 			free_blk(fs, *bkref);
1058 	}
1059 	// block in indirect block in double indirect block
1060 	else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE/4 - 1))
1061 	{
1062 		bw->bpdind++;
1063 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1064 		b = (uint32*)get_blk(fs, b[bw->bpind]);
1065 		bkref = &b[bw->bpdind];
1066 		if(extend) // allocate block
1067 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1068 		if(reduce) // free block
1069 			free_blk(fs, *bkref);
1070 	}
1071 	// first block in indirect block in double indirect block
1072 	else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
1073 	{
1074 		bw->bnum++;
1075 		bw->bpdind = 0;
1076 		bw->bpind++;
1077 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1078 		if(extend) // allocate indirect block
1079 			b[bw->bpind] = alloc_blk(fs,nod);
1080 		if(reduce) // free indirect block
1081 			free_blk(fs, b[bw->bpind]);
1082 		b = (uint32*)get_blk(fs, b[bw->bpind]);
1083 		bkref = &b[bw->bpdind];
1084 		if(extend) // allocate first block
1085 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1086 		if(reduce) // free first block
1087 			free_blk(fs, *bkref);
1088 	}
1089 
1090 	/* Adding support for triple indirection */
1091 	/* Just starting triple indirection. Allocate the indirection
1092 	   blocks and the first data block
1093 	 */
1094 	else if (bw->bpdir == EXT2_DIND_BLOCK)
1095 	{
1096 	  	bw->bnum += 3;
1097 		bw->bpdir = EXT2_TIND_BLOCK;
1098 		bw->bpind = 0;
1099 		bw->bpdind = 0;
1100 		bw->bptind = 0;
1101 		if(extend) // allocate triple indirect block
1102 			get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
1103 		if(reduce) // free triple indirect block
1104 			free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1105 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1106 		if(extend) // allocate first double indirect block
1107 			b[bw->bpind] = alloc_blk(fs,nod);
1108 		if(reduce) // free first double indirect block
1109 			free_blk(fs, b[bw->bpind]);
1110 		b = (uint32*)get_blk(fs, b[bw->bpind]);
1111 		if(extend) // allocate first indirect block
1112 			b[bw->bpdind] = alloc_blk(fs,nod);
1113 		if(reduce) // free first indirect block
1114 			free_blk(fs, b[bw->bpind]);
1115 		b = (uint32*)get_blk(fs, b[bw->bpdind]);
1116 		bkref = &b[bw->bptind];
1117 		if(extend) // allocate first data block
1118 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1119 		if(reduce) // free first block
1120 			free_blk(fs, *bkref);
1121 	}
1122 	/* Still processing a single indirect block down the indirection
1123 	   chain.Allocate a data block for it
1124 	 */
1125 	else if ( (bw->bpdir == EXT2_TIND_BLOCK) &&
1126 		  (bw->bptind < BLOCKSIZE/4 -1) )
1127 	{
1128 		bw->bptind++;
1129 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1130 		b = (uint32*)get_blk(fs, b[bw->bpind]);
1131 		b = (uint32*)get_blk(fs, b[bw->bpdind]);
1132 		bkref = &b[bw->bptind];
1133 		if(extend) // allocate data block
1134 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1135 		if(reduce) // free block
1136 			free_blk(fs, *bkref);
1137 	}
1138 	/* Finished processing a single indirect block. But still in the
1139 	   same double indirect block. Allocate new single indirect block
1140 	   for it and a data block
1141 	 */
1142 	else if ( (bw->bpdir == EXT2_TIND_BLOCK) &&
1143 		  (bw->bpdind < BLOCKSIZE/4 -1) )
1144 	{
1145 		bw->bnum++;
1146 		bw->bptind = 0;
1147 		bw->bpdind++;
1148 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1149 		b = (uint32*)get_blk(fs, b[bw->bpind]);
1150 		if(extend) // allocate single indirect block
1151 			b[bw->bpdind] = alloc_blk(fs,nod);
1152 		if(reduce) // free indirect block
1153 			free_blk(fs, b[bw->bpind]);
1154 		b = (uint32*)get_blk(fs, b[bw->bpdind]);
1155 		bkref = &b[bw->bptind];
1156 		if(extend) // allocate first data block
1157 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1158 		if(reduce) // free first block
1159 			free_blk(fs, *bkref);
1160 	}
1161 	/* Finished processing a double indirect block. Allocate the next
1162 	   double indirect block and the single,data blocks for it
1163 	 */
1164 	else if ( (bw->bpdir == EXT2_TIND_BLOCK) &&
1165 		  (bw->bpind < BLOCKSIZE/4 - 1) )
1166 	{
1167 		bw->bnum += 2;
1168 		bw->bpdind = 0;
1169 		bw->bptind = 0;
1170 		bw->bpind++;
1171 		b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1172 		if(extend) // allocate double indirect block
1173 			b[bw->bpind] = alloc_blk(fs,nod);
1174 		if(reduce) // free double indirect block
1175 			free_blk(fs, b[bw->bpind]);
1176 		b = (uint32*)get_blk(fs, b[bw->bpind]);
1177 		if(extend) // allocate single indirect block
1178 			b[bw->bpdind] = alloc_blk(fs,nod);
1179 		if(reduce) // free indirect block
1180 			free_blk(fs, b[bw->bpind]);
1181 		b = (uint32*)get_blk(fs, b[bw->bpdind]);
1182 		bkref = &b[bw->bptind];
1183 		if(extend) // allocate first block
1184 			*bkref = hole ? 0 : alloc_blk(fs,nod);
1185 		if(reduce) // free first block
1186 			free_blk(fs, *bkref);
1187 	}
1188 	else
1189 		error_msg_and_die("file too big !");
1190 	/* End change for walking triple indirection */
1191 
1192 	if(*bkref)
1193 	{
1194 		bw->bnum++;
1195 		if(!reduce && !allocated(GRP_GET_BLOCK_BITMAP(fs,*bkref), GRP_BBM_OFFSET(fs,*bkref)))
1196 			error_msg_and_die("[block %d of inode %d is unallocated !]", *bkref, nod);
1197 	}
1198 	if(extend)
1199 		get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK;
1200 	return *bkref;
1201 }
1202 
1203 // add blocks to an inode (file/dir/etc...)
1204 static void
extend_blk(filesystem * fs,uint32 nod,block b,int amount)1205 extend_blk(filesystem *fs, uint32 nod, block b, int amount)
1206 {
1207 	int create = amount;
1208 	blockwalker bw, lbw;
1209 	uint32 bk;
1210 	init_bw(&bw);
1211 	if(amount < 0)
1212 	{
1213 		uint32 i;
1214 		for(i = 0; i < get_nod(fs, nod)->i_blocks / INOBLK + amount; i++)
1215 			walk_bw(fs, nod, &bw, 0, 0);
1216 		while(walk_bw(fs, nod, &bw, &create, 0) != WALK_END)
1217 			/*nop*/;
1218 		get_nod(fs, nod)->i_blocks += amount * INOBLK;
1219 	}
1220 	else
1221 	{
1222 		lbw = bw;
1223 		while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
1224 			lbw = bw;
1225 		bw = lbw;
1226 		while(create)
1227 		{
1228 			int i, copyb = 0;
1229 			if(!(fs->sb.s_reserved[200] & OP_HOLES))
1230 				copyb = 1;
1231 			else
1232 				for(i = 0; i < BLOCKSIZE / 4; i++)
1233 					if(((int32*)(b + BLOCKSIZE * (amount - create)))[i])
1234 					{
1235 						copyb = 1;
1236 						break;
1237 					}
1238 			if((bk = walk_bw(fs, nod, &bw, &create, !copyb)) == WALK_END)
1239 				break;
1240 			if(copyb)
1241 				memcpy(get_blk(fs, bk), b + BLOCKSIZE * (amount - create - 1), BLOCKSIZE);
1242 		}
1243 	}
1244 }
1245 
1246 // link an entry (inode #) to a directory
1247 static void
add2dir(filesystem * fs,uint32 dnod,uint32 nod,const char * name)1248 add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name)
1249 {
1250 	blockwalker bw;
1251 	uint32 bk;
1252 	uint8 *b;
1253 	directory *d;
1254 	int reclen, nlen;
1255 	inode *node;
1256 	inode *pnode;
1257 
1258 	pnode = get_nod(fs, dnod);
1259 	if((pnode->i_mode & FM_IFMT) != FM_IFDIR)
1260 		error_msg_and_die("can't add '%s' to a non-directory", name);
1261 	if(!*name)
1262 		error_msg_and_die("can't create an inode with an empty name");
1263 	if(strchr(name, '/'))
1264 		error_msg_and_die("bad name '%s' (contains a slash)", name);
1265 	nlen = strlen(name);
1266 	reclen = sizeof(directory) + rndup(nlen, 4);
1267 	if(reclen > BLOCKSIZE)
1268 		error_msg_and_die("bad name '%s' (too long)", name);
1269 	init_bw(&bw);
1270 	while((bk = walk_bw(fs, dnod, &bw, 0, 0)) != WALK_END) // for all blocks in dir
1271 	{
1272 		b = get_blk(fs, bk);
1273 		// for all dir entries in block
1274 		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
1275 		{
1276 			// if empty dir entry, large enough, use it
1277 			if((!d->d_inode) && (d->d_rec_len >= reclen))
1278 			{
1279 				d->d_inode = nod;
1280 				node = get_nod(fs, nod);
1281 				node->i_links_count++;
1282 				d->d_name_len = nlen;
1283 				strncpy(d->d_name, name, nlen);
1284 				return;
1285 			}
1286 			// if entry with enough room (last one?), shrink it & use it
1287 			if(d->d_rec_len >= (sizeof(directory) + rndup(d->d_name_len, 4) + reclen))
1288 			{
1289 				reclen = d->d_rec_len;
1290 				d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
1291 				reclen -= d->d_rec_len;
1292 				d = (directory*) (((int8*)d) + d->d_rec_len);
1293 				d->d_rec_len = reclen;
1294 				d->d_inode = nod;
1295 				node = get_nod(fs, nod);
1296 				node->i_links_count++;
1297 				d->d_name_len = nlen;
1298 				strncpy(d->d_name, name, nlen);
1299 				return;
1300 			}
1301 		}
1302 	}
1303 	// we found no free entry in the directory, so we add a block
1304 	if(!(b = get_workblk()))
1305 		error_msg_and_die("get_workblk() failed.");
1306 	d = (directory*)b;
1307 	d->d_inode = nod;
1308 	node = get_nod(fs, nod);
1309 	node->i_links_count++;
1310 	d->d_rec_len = BLOCKSIZE;
1311 	d->d_name_len = nlen;
1312 	strncpy(d->d_name, name, nlen);
1313 	extend_blk(fs, dnod, b, 1);
1314 	get_nod(fs, dnod)->i_size += BLOCKSIZE;
1315 	free_workblk(b);
1316 }
1317 
1318 // find an entry in a directory
1319 static uint32
find_dir(filesystem * fs,uint32 nod,const char * name)1320 find_dir(filesystem *fs, uint32 nod, const char * name)
1321 {
1322 	blockwalker bw;
1323 	uint32 bk;
1324 	int nlen = strlen(name);
1325 	init_bw(&bw);
1326 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
1327 	{
1328 		directory *d;
1329 		uint8 *b;
1330 		b = get_blk(fs, bk);
1331 		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
1332 			if(d->d_inode && (nlen == d->d_name_len) && !strncmp(d->d_name, name, nlen))
1333 				return d->d_inode;
1334 	}
1335 	return 0;
1336 }
1337 
1338 // find the inode of a full path
1339 static uint32
find_path(filesystem * fs,uint32 nod,const char * name)1340 find_path(filesystem *fs, uint32 nod, const char * name)
1341 {
1342 	char *p, *n, *n2 = xstrdup(name);
1343 	n = n2;
1344 	while(*n == '/')
1345 	{
1346 		nod = EXT2_ROOT_INO;
1347 		n++;
1348 	}
1349 	while(*n)
1350 	{
1351 		if((p = strchr(n, '/')))
1352 			(*p) = 0;
1353 		if(!(nod = find_dir(fs, nod, n)))
1354 			break;
1355 		if(p)
1356 			n = p + 1;
1357 		else
1358 			break;
1359 	}
1360 	free(n2);
1361 	return nod;
1362 }
1363 
1364 // chmod an inode
1365 void
chmod_fs(filesystem * fs,uint32 nod,uint16 mode,uint16 uid,uint16 gid)1366 chmod_fs(filesystem *fs, uint32 nod, uint16 mode, uint16 uid, uint16 gid)
1367 {
1368 	inode *node;
1369 	node = get_nod(fs, nod);
1370 	node->i_mode = (node->i_mode & ~FM_IMASK) | (mode & FM_IMASK);
1371 	node->i_uid = uid;
1372 	node->i_gid = gid;
1373 }
1374 
1375 // create a simple inode
1376 static uint32
mknod_fs(filesystem * fs,uint32 parent_nod,const char * name,uint16 mode,uint16 uid,uint16 gid,uint8 major,uint8 minor,uint32 ctime,uint32 mtime)1377 mknod_fs(filesystem *fs, uint32 parent_nod, const char *name, uint16 mode, uint16 uid, uint16 gid, uint8 major, uint8 minor, uint32 ctime, uint32 mtime)
1378 {
1379 	uint32 nod;
1380 	inode *node;
1381 	{
1382 		nod = alloc_nod(fs);
1383 		node = get_nod(fs, nod);
1384 		node->i_mode = mode;
1385 		add2dir(fs, parent_nod, nod, name);
1386 		switch(mode & FM_IFMT)
1387 		{
1388 			case FM_IFLNK:
1389 				mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
1390 				break;
1391 			case FM_IFBLK:
1392 			case FM_IFCHR:
1393 				((uint8*)get_nod(fs, nod)->i_block)[0] = minor;
1394 				((uint8*)get_nod(fs, nod)->i_block)[1] = major;
1395 				break;
1396 			case FM_IFDIR:
1397 				add2dir(fs, nod, nod, ".");
1398 				add2dir(fs, nod, parent_nod, "..");
1399 				fs->gd[GRP_GROUP_OF_INODE(fs,nod)].bg_used_dirs_count++;
1400 				break;
1401 		}
1402 	}
1403 	node->i_uid = uid;
1404 	node->i_gid = gid;
1405 	node->i_atime = mtime;
1406 	node->i_ctime = ctime;
1407 	node->i_mtime = mtime;
1408 	return nod;
1409 }
1410 
1411 // make a full-fledged directory (i.e. with "." & "..")
1412 static inline uint32
mkdir_fs(filesystem * fs,uint32 parent_nod,const char * name,uint32 mode,uid_t uid,gid_t gid,uint32 ctime,uint32 mtime)1413 mkdir_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode,
1414 	uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
1415 {
1416 	return mknod_fs(fs, parent_nod, name, mode|FM_IFDIR, uid, gid, 0, 0, ctime, mtime);
1417 }
1418 
1419 // make a symlink
1420 static uint32
mklink_fs(filesystem * fs,uint32 parent_nod,const char * name,size_t size,uint8 * b,uid_t uid,gid_t gid,uint32 ctime,uint32 mtime)1421 mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 *b, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
1422 {
1423 	uint32 nod = mknod_fs(fs, parent_nod, name, FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO, uid, gid, 0, 0, ctime, mtime);
1424 	extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
1425 	get_nod(fs, nod)->i_size = size;
1426 	if(size <= 4 * (EXT2_TIND_BLOCK+1))
1427 	{
1428 		strncpy((char*)get_nod(fs, nod)->i_block, (char*)b, size);
1429 		return nod;
1430 	}
1431 	extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
1432 	return nod;
1433 }
1434 
1435 // make a file from a FILE*
1436 static uint32
mkfile_fs(filesystem * fs,uint32 parent_nod,const char * name,uint32 mode,size_t size,FILE * f,uid_t uid,gid_t gid,uint32 ctime,uint32 mtime)1437 mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
1438 {
1439 	uint8 * b;
1440 	uint32 nod = mknod_fs(fs, parent_nod, name, mode|FM_IFREG, uid, gid, 0, 0, ctime, mtime);
1441 	extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
1442 	get_nod(fs, nod)->i_size = size;
1443 	if (size) {
1444 		if(!(b = (uint8*)calloc(rndup(size, BLOCKSIZE), 1)))
1445 			error_msg_and_die("not enough mem to read file '%s'", name);
1446 		if(f)
1447 			fread(b, size, 1, f); // FIXME: ugly. use mmap() ...
1448 		extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
1449 		free(b);
1450 	}
1451 	return nod;
1452 }
1453 
1454 // retrieves a mode info from a struct stat
1455 static uint32
get_mode(struct stat * st)1456 get_mode(struct stat *st)
1457 {
1458 	uint32 mode = 0;
1459 
1460 	if(st->st_mode & S_IRUSR)
1461 		mode |= FM_IRUSR;
1462 	if(st->st_mode & S_IWUSR)
1463 		mode |= FM_IWUSR;
1464 	if(st->st_mode & S_IXUSR)
1465 		mode |= FM_IXUSR;
1466 	if(st->st_mode & S_IRGRP)
1467 		mode |= FM_IRGRP;
1468 	if(st->st_mode & S_IWGRP)
1469 		mode |= FM_IWGRP;
1470 	if(st->st_mode & S_IXGRP)
1471 		mode |= FM_IXGRP;
1472 	if(st->st_mode & S_IROTH)
1473 		mode |= FM_IROTH;
1474 	if(st->st_mode & S_IWOTH)
1475 		mode |= FM_IWOTH;
1476 	if(st->st_mode & S_IXOTH)
1477 		mode |= FM_IXOTH;
1478 	if(st->st_mode & S_ISUID)
1479 		mode |= FM_ISUID;
1480 	if(st->st_mode & S_ISGID)
1481 		mode |= FM_ISGID;
1482 	if(st->st_mode & S_ISVTX)
1483 		mode |= FM_ISVTX;
1484 	return mode;
1485 }
1486 
1487 // add or fixup entries to the filesystem from a text file
1488 /*  device table entries take the form of:
1489     <path>	<type> <mode>	<uid>	<gid>	<major>	<minor>	<start>	<inc>	<count>
1490     /dev/mem     c    640       0       0         1       1       0     0         -
1491 
1492     type can be one of:
1493 	f	A regular file
1494 	d	Directory
1495 	c	Character special device file
1496 	b	Block special device file
1497 	p	Fifo (named pipe)
1498 
1499     I don't bother with symlinks (permissions are irrelevant), hard
1500     links (special cases of regular files), or sockets (why bother).
1501 
1502     Regular files must exist in the target root directory.  If a char,
1503     block, fifo, or directory does not exist, it will be created.
1504 */
1505 
1506 static void
add2fs_from_file(filesystem * fs,uint32 this_nod,FILE * fh,uint32 fs_timestamp,struct stats * stats)1507 add2fs_from_file(filesystem *fs, uint32 this_nod, FILE * fh, uint32 fs_timestamp, struct stats *stats)
1508 {
1509 	unsigned long mode, uid, gid, major, minor;
1510 	unsigned long start, increment, count;
1511 	uint32 nod, ctime, mtime;
1512 	char *c, type, *path = NULL, *path2 = NULL, *dir, *name, *line = NULL;
1513 	size_t len;
1514 	struct stat st;
1515 	int nbargs, lineno = 0;
1516 
1517 	fstat(fileno(fh), &st);
1518 	ctime = fs_timestamp;
1519 	mtime = st.st_mtime;
1520 	while(getline(&line, &len, fh) >= 0)
1521 	{
1522 		mode = uid = gid = major = minor = 0;
1523 		start = 0; increment = 1; count = 0;
1524 		lineno++;
1525 		if((c = strchr(line, '#')))
1526 			*c = 0;
1527 		if (path) {
1528 			free(path);
1529 			path = NULL;
1530 		}
1531 		if (path2) {
1532 			free(path2);
1533 			path2 = NULL;
1534 		}
1535 		nbargs = sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu",
1536 					SCANF_STRING(path), &type, &mode, &uid, &gid, &major, &minor,
1537 					&start, &increment, &count);
1538 		if(nbargs < 3)
1539 		{
1540 			if(nbargs > 0)
1541 				error_msg("device table line %d skipped: bad format for entry '%s'", lineno, path);
1542 			continue;
1543 		}
1544 		mode &= FM_IMASK;
1545 		path2 = strdup(path);
1546 		name = basename(path);
1547 		dir = dirname(path2);
1548 		if((!strcmp(name, ".")) || (!strcmp(name, "..")))
1549 		{
1550 			error_msg("device table line %d skipped", lineno);
1551 			continue;
1552 		}
1553 		if(fs)
1554 		{
1555 			if(!(nod = find_path(fs, this_nod, dir)))
1556 			{
1557 				error_msg("device table line %d skipped: can't find directory '%s' to create '%s''", lineno, dir, name);
1558 				continue;
1559 			}
1560 		}
1561 		else
1562 			nod = 0;
1563 		switch (type)
1564 		{
1565 			case 'd':
1566 				mode |= FM_IFDIR;
1567 				break;
1568 			case 'f':
1569 				mode |= FM_IFREG;
1570 				break;
1571 			case 'p':
1572 				mode |= FM_IFIFO;
1573 				break;
1574 			case 's':
1575 				mode |= FM_IFSOCK;
1576 				break;
1577 			case 'c':
1578 				mode |= FM_IFCHR;
1579 				break;
1580 			case 'b':
1581 				mode |= FM_IFBLK;
1582 				break;
1583 			default:
1584 				error_msg("device table line %d skipped: bad type '%c' for entry '%s'", lineno, type, name);
1585 				continue;
1586 		}
1587 		if(stats) {
1588 			if(count > 0)
1589 				stats->ninodes += count - start;
1590 			else
1591 				stats->ninodes++;
1592 		} else {
1593 			if(count > 0)
1594 			{
1595 				char *dname;
1596 				unsigned long i;
1597 				unsigned len;
1598 				len = strlen(name) + 10;
1599 				dname = malloc(len + 1);
1600 				for(i = start; i < count; i++)
1601 				{
1602 					uint32 oldnod;
1603 					SNPRINTF(dname, len, "%s%lu", name, i);
1604 					oldnod = find_dir(fs, nod, dname);
1605 					if(oldnod)
1606 						chmod_fs(fs, oldnod, mode, uid, gid);
1607 					else
1608 						mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
1609 				}
1610 				free(dname);
1611 			}
1612 			else
1613 			{
1614 				uint32 oldnod = find_dir(fs, nod, name);
1615 				if(oldnod)
1616 					chmod_fs(fs, oldnod, mode, uid, gid);
1617 				else
1618 					mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
1619 			}
1620 		}
1621 	}
1622 	if (line)
1623 		free(line);
1624 	if (path)
1625 		free(path);
1626 	if (path2)
1627 		free(path2);
1628 }
1629 
1630 static void
prep_stat(const char * root_path)1631 prep_stat(const char *root_path)
1632 {
1633 	int len = strlen(root_path);
1634 
1635 	if((len >= 4) && (!strcmp(root_path + len - 4, "data"))) {
1636 		source_path_len = len - 4;
1637 	} else if((len >= 7) && (!strcmp(root_path + len - 6, "system"))) {
1638 		source_path_len = len - 6;
1639 	} else {
1640 		error_msg_and_die("Fixstats (-a) option requested but "
1641 				  "filesystem is not data or android!");
1642 	}
1643 }
1644 
1645 static void
fix_stat(const char * path,struct stat * s)1646 fix_stat(const char *path, struct stat *s)
1647 {
1648 	path += source_path_len;
1649 	fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
1650 }
1651 
1652 // adds a tree of entries to the filesystem from current dir
1653 static void
add2fs_from_dir(filesystem * fs,const char * path,uint32 this_nod,int squash_uids,int squash_perms,int fixstats,uint32 fs_timestamp,struct stats * stats)1654 add2fs_from_dir(filesystem *fs, const char *path, uint32 this_nod, int squash_uids, int squash_perms, int fixstats, uint32 fs_timestamp, struct stats *stats)
1655 {
1656 	uint32 nod;
1657 	uint32 uid, gid, mode, ctime, mtime;
1658 	const char *name;
1659 	FILE *fh;
1660 	DIR *dh;
1661 	struct dirent *dent;
1662 	struct stat st;
1663 	char *lnk;
1664 	uint32 save_nod;
1665 	char full_name[2048];
1666 
1667 	if(!(dh = opendir(".")))
1668 		perror_msg_and_die(".");
1669 	while((dent = readdir(dh)))
1670 	{
1671 		if((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
1672 			continue;
1673 
1674 		lstat(dent->d_name, &st);
1675 
1676 		if(fixstats) {
1677 			int tmp = snprintf(full_name, sizeof(full_name),
1678 			                   "%s/%s", path, dent->d_name);
1679 			if(tmp >= (int)sizeof(full_name))
1680 				error_msg_and_die("Path too long!");
1681 			fix_stat(full_name, &st);
1682 		} else
1683 			full_name[0] = '\0';
1684 		uid = st.st_uid;
1685 		gid = st.st_gid;
1686 		ctime = fs_timestamp;
1687 		mtime = st.st_mtime;
1688 		name = dent->d_name;
1689 		mode = get_mode(&st);
1690 		if(squash_uids)
1691 			uid = gid = 0;
1692 		if(squash_perms)
1693 			mode &= ~(FM_IRWXG | FM_IRWXO);
1694 		if(stats)
1695 			switch(st.st_mode & S_IFMT)
1696 			{
1697 				case S_IFLNK:
1698 				case S_IFREG:
1699 					if((st.st_mode & S_IFMT) == S_IFREG || st.st_size > 4 * (EXT2_TIND_BLOCK+1))
1700 						stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
1701 				case S_IFCHR:
1702 				case S_IFBLK:
1703 				case S_IFIFO:
1704 				case S_IFSOCK:
1705 					stats->ninodes++;
1706 					break;
1707 				case S_IFDIR:
1708 					stats->ninodes++;
1709 					if(chdir(dent->d_name) < 0)
1710 						perror_msg_and_die(dent->d_name);
1711 					add2fs_from_dir(fs, full_name, this_nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
1712 					chdir("..");
1713 					break;
1714 				default:
1715 					break;
1716 			}
1717 		else
1718 		{
1719 			if((nod = find_dir(fs, this_nod, name)))
1720 			{
1721 				error_msg("ignoring duplicate entry %s", name);
1722 				if(S_ISDIR(st.st_mode)) {
1723 					if(chdir(dent->d_name) < 0)
1724 						perror_msg_and_die(name);
1725 					add2fs_from_dir(fs, full_name, nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
1726 					chdir("..");
1727 				}
1728 				continue;
1729 			}
1730 			save_nod = 0;
1731 			/* Check for hardlinks */
1732 			if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && st.st_nlink > 1) {
1733 				int32 hdlink = is_hardlink(st.st_ino);
1734 				if (hdlink >= 0) {
1735 					add2dir(fs, this_nod, hdlinks.hdl[hdlink].dst_nod, name);
1736 					continue;
1737 				} else {
1738 					save_nod = 1;
1739 				}
1740 			}
1741 			switch(st.st_mode & S_IFMT)
1742 			{
1743 #if HAVE_STRUCT_STAT_ST_RDEV
1744 				case S_IFCHR:
1745 					nod = mknod_fs(fs, this_nod, name, mode|FM_IFCHR, uid, gid, major(st.st_rdev), minor(st.st_rdev), ctime, mtime);
1746 					break;
1747 				case S_IFBLK:
1748 					nod = mknod_fs(fs, this_nod, name, mode|FM_IFBLK, uid, gid, major(st.st_rdev), minor(st.st_rdev), ctime, mtime);
1749 					break;
1750 #endif
1751 				case S_IFIFO:
1752 					nod = mknod_fs(fs, this_nod, name, mode|FM_IFIFO, uid, gid, 0, 0, ctime, mtime);
1753 					break;
1754 				case S_IFSOCK:
1755 					nod = mknod_fs(fs, this_nod, name, mode|FM_IFSOCK, uid, gid, 0, 0, ctime, mtime);
1756 					break;
1757 				case S_IFLNK:
1758 					lnk = xreadlink(dent->d_name);
1759 					mklink_fs(fs, this_nod, name, st.st_size, (uint8*)lnk, uid, gid, ctime, mtime);
1760 					free(lnk);
1761 					break;
1762 				case S_IFREG:
1763 					fh = xfopen(dent->d_name, "rb");
1764 					nod = mkfile_fs(fs, this_nod, name, mode, st.st_size, fh, uid, gid, ctime, mtime);
1765 					fclose(fh);
1766 					break;
1767 				case S_IFDIR:
1768 					nod = mkdir_fs(fs, this_nod, name, mode, uid, gid, ctime, mtime);
1769 					if(chdir(dent->d_name) < 0)
1770 						perror_msg_and_die(name);
1771 					add2fs_from_dir(fs, full_name, nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
1772 					chdir("..");
1773 					break;
1774 				default:
1775 					error_msg("ignoring entry %s", name);
1776 			}
1777 			if (save_nod) {
1778 				if (hdlinks.count == hdlink_cnt) {
1779 					if ((hdlinks.hdl =
1780 						 realloc (hdlinks.hdl, (hdlink_cnt + HDLINK_CNT) *
1781 								  sizeof (struct hdlink_s))) == NULL) {
1782 						error_msg_and_die("Not enough memory");
1783 					}
1784 					hdlink_cnt += HDLINK_CNT;
1785 				}
1786 				hdlinks.hdl[hdlinks.count].src_inode = st.st_ino;
1787 				hdlinks.hdl[hdlinks.count].dst_nod = nod;
1788 				hdlinks.count++;
1789 			}
1790 		}
1791 	}
1792 	closedir(dh);
1793 }
1794 
1795 // endianness swap of x-indirect blocks
1796 static void
swap_goodblocks(filesystem * fs,inode * nod)1797 swap_goodblocks(filesystem *fs, inode *nod)
1798 {
1799 	uint32 i,j;
1800 	int done=0;
1801 	uint32 *b,*b2;
1802 
1803 	uint32 nblk = nod->i_blocks / INOBLK;
1804 	if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
1805 		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
1806 			nod->i_block[i] = swab32(nod->i_block[i]);
1807 	if(nblk <= EXT2_IND_BLOCK)
1808 		return;
1809 	swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
1810 	if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
1811 		return;
1812 	/* Currently this will fail b'cos the number of blocks as stored
1813 	   in i_blocks also includes the indirection blocks (see
1814 	   walk_bw). But this function assumes that i_blocks only
1815 	   stores the count of data blocks ( Actually according to
1816 	   "Understanding the Linux Kernel" (Table 17-3 p502 1st Ed)
1817 	   i_blocks IS supposed to store the count of data blocks). so
1818 	   with a file of size 268K nblk would be 269.The above check
1819 	   will be false even though double indirection hasn't been
1820 	   started.This is benign as 0 means block 0 which has been
1821 	   zeroed out and therefore points back to itself from any offset
1822 	 */
1823 	// FIXME: I have fixed that, but I have the feeling the rest of
1824 	// ths function needs to be fixed for the same reasons - Xav
1825 	assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
1826 	for(i = 0; i < BLOCKSIZE/4; i++)
1827 		if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
1828 			swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
1829 	swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
1830 	if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
1831 		return;
1832 	/* Adding support for triple indirection */
1833 	b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
1834 	for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
1835 		b2 = (uint32*)get_blk(fs,b[i]);
1836 		for(j=0; j<BLOCKSIZE/4;j++) {
1837 			if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 +
1838 				     (BLOCKSIZE/4)*(BLOCKSIZE/4) +
1839 				     i*(BLOCKSIZE/4)*(BLOCKSIZE/4) +
1840 				     j*(BLOCKSIZE/4)) )
1841 			  swap_block(get_blk(fs,b2[j]));
1842 			else {
1843 			  done = 1;
1844 			  break;
1845 			}
1846 		}
1847 		swap_block((uint8 *)b2);
1848 	}
1849 	swap_block((uint8 *)b);
1850 	return;
1851 }
1852 
1853 static void
swap_badblocks(filesystem * fs,inode * nod)1854 swap_badblocks(filesystem *fs, inode *nod)
1855 {
1856 	uint32 i,j;
1857 	int done=0;
1858 	uint32 *b,*b2;
1859 
1860 	uint32 nblk = nod->i_blocks / INOBLK;
1861 	if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
1862 		for(i = 0; i <= EXT2_TIND_BLOCK; i++)
1863 			nod->i_block[i] = swab32(nod->i_block[i]);
1864 	if(nblk <= EXT2_IND_BLOCK)
1865 		return;
1866 	swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
1867 	if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
1868 		return;
1869 	/* See comment in swap_goodblocks */
1870 	assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
1871 	swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
1872 	for(i = 0; i < BLOCKSIZE/4; i++)
1873 		if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
1874 			swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
1875 	if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
1876 		return;
1877 	/* Adding support for triple indirection */
1878 	b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
1879 	swap_block((uint8 *)b);
1880 	for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
1881 		b2 = (uint32*)get_blk(fs,b[i]);
1882 		swap_block((uint8 *)b2);
1883 		for(j=0; j<BLOCKSIZE/4;j++) {
1884 			if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 +
1885 				     (BLOCKSIZE/4)*(BLOCKSIZE/4) +
1886 				     i*(BLOCKSIZE/4)*(BLOCKSIZE/4) +
1887 				     j*(BLOCKSIZE/4)) )
1888 			  swap_block(get_blk(fs,b2[j]));
1889 			else {
1890 			  done = 1;
1891 			  break;
1892 			}
1893 		}
1894 	}
1895 	return;
1896 }
1897 
1898 // endianness swap of the whole filesystem
1899 static void
swap_goodfs(filesystem * fs)1900 swap_goodfs(filesystem *fs)
1901 {
1902 	uint32 i;
1903 	for(i = 1; i < fs->sb.s_inodes_count; i++)
1904 	{
1905 		inode *nod = get_nod(fs, i);
1906 		if(nod->i_mode & FM_IFDIR)
1907 		{
1908 			blockwalker bw;
1909 			uint32 bk;
1910 			init_bw(&bw);
1911 			while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
1912 			{
1913 				directory *d;
1914 				uint8 *b;
1915 				b = get_blk(fs, bk);
1916 				for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + swab16(d->d_rec_len)))
1917 					swap_dir(d);
1918 			}
1919 		}
1920 		swap_goodblocks(fs, nod);
1921 		swap_nod(nod);
1922 	}
1923 	for(i=0;i<GRP_NBGROUPS(fs);i++)
1924 		swap_gd(&(fs->gd[i]));
1925 	swap_sb(&fs->sb);
1926 }
1927 
1928 static void
swap_badfs(filesystem * fs)1929 swap_badfs(filesystem *fs)
1930 {
1931 	uint32 i;
1932 	swap_sb(&fs->sb);
1933 	for(i=0;i<GRP_NBGROUPS(fs);i++)
1934 		swap_gd(&(fs->gd[i]));
1935 	for(i = 1; i < fs->sb.s_inodes_count; i++)
1936 	{
1937 		inode *nod = get_nod(fs, i);
1938 		swap_nod(nod);
1939 		swap_badblocks(fs, nod);
1940 		if(nod->i_mode & FM_IFDIR)
1941 		{
1942 			blockwalker bw;
1943 			uint32 bk;
1944 			init_bw(&bw);
1945 			while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
1946 			{
1947 				directory *d;
1948 				uint8 *b;
1949 				b = get_blk(fs, bk);
1950 				for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
1951 					swap_dir(d);
1952 			}
1953 		}
1954 	}
1955 }
1956 
1957 // initialize an empty filesystem
1958 static filesystem *
init_fs(int nbblocks,int nbinodes,int nbresrvd,int holes,uint32 fs_timestamp)1959 init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp)
1960 {
1961 	uint32 i;
1962 	filesystem *fs;
1963 	directory *d;
1964 	uint8 * b;
1965 	uint32 nod, first_block;
1966 	uint32 nbgroups,nbinodes_per_group,overhead_per_group,free_blocks,
1967 		free_blocks_per_group,nbblocks_per_group,min_nbgroups;
1968 	uint32 gdsz,itblsz,bbmpos,ibmpos,itblpos;
1969 	uint32 j;
1970 	uint8 *bbm,*ibm;
1971 	inode *itab0;
1972 
1973 	if(nbresrvd < 0)
1974 		error_msg_and_die("reserved blocks value is invalid. Note: options have changed, see --help or the man page.");
1975 	if(nbinodes < EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0))
1976 		error_msg_and_die("too few inodes. Note: options have changed, see --help or the man page.");
1977 	if(nbblocks < 8)
1978 		error_msg_and_die("too few blocks. Note: options have changed, see --help or the man page.");
1979 
1980 	/* nbinodes is the total number of inodes in the system.
1981 	 * a block group can have no more than 8192 inodes.
1982 	 */
1983 	min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP;
1984 
1985 	/* nbblocks is the total number of blocks in the filesystem.
1986 	 * a block group can have no more than 8192 blocks.
1987 	 */
1988 	first_block = (BLOCKSIZE == 1024);
1989 	nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP;
1990 	if(nbgroups < min_nbgroups) nbgroups = min_nbgroups;
1991 	nbblocks_per_group = rndup((nbblocks - first_block + nbgroups - 1)/nbgroups, 8);
1992 	nbinodes_per_group = rndup((nbinodes + nbgroups - 1)/nbgroups,
1993 						(BLOCKSIZE/sizeof(inode)));
1994 	if (nbinodes_per_group < 16)
1995 		nbinodes_per_group = 16; //minimum number b'cos the first 10 are reserved
1996 
1997 	gdsz = rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE;
1998 	itblsz = nbinodes_per_group * sizeof(inode)/BLOCKSIZE;
1999 	overhead_per_group = 3 /*sb,bbm,ibm*/ + gdsz + itblsz;
2000 	if((uint32)nbblocks - 1 < overhead_per_group * nbgroups)
2001 		error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
2002 	free_blocks = nbblocks - overhead_per_group*nbgroups - 1 /*boot block*/;
2003 	free_blocks_per_group = nbblocks_per_group - overhead_per_group;
2004 
2005 	if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
2006 		error_msg_and_die("not enough memory for filesystem");
2007 
2008 	// create the superblock for an empty filesystem
2009 	fs->sb.s_inodes_count = nbinodes_per_group * nbgroups;
2010 	fs->sb.s_blocks_count = nbblocks;
2011 	fs->sb.s_r_blocks_count = nbresrvd;
2012 	fs->sb.s_free_blocks_count = free_blocks;
2013 	fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1;
2014 	fs->sb.s_first_data_block = first_block;
2015 	fs->sb.s_log_block_size = BLOCKSIZE >> 11;
2016 	fs->sb.s_log_frag_size = BLOCKSIZE >> 11;
2017 	fs->sb.s_blocks_per_group = nbblocks_per_group;
2018 	fs->sb.s_frags_per_group = nbblocks_per_group;
2019 	fs->sb.s_inodes_per_group = nbinodes_per_group;
2020 	fs->sb.s_wtime = fs_timestamp;
2021 	fs->sb.s_magic = EXT2_MAGIC_NUMBER;
2022 	fs->sb.s_lastcheck = fs_timestamp;
2023 
2024 	// set up groupdescriptors
2025 	for(i=0, bbmpos=gdsz+2, ibmpos=bbmpos+1, itblpos=ibmpos+1;
2026 		i<nbgroups;
2027 		i++, bbmpos+=nbblocks_per_group, ibmpos+=nbblocks_per_group, itblpos+=nbblocks_per_group)
2028 	{
2029 		if(free_blocks > free_blocks_per_group) {
2030 			fs->gd[i].bg_free_blocks_count = free_blocks_per_group;
2031 			free_blocks -= free_blocks_per_group;
2032 		} else {
2033 			fs->gd[i].bg_free_blocks_count = free_blocks;
2034 			free_blocks = 0; // this is the last block group
2035 		}
2036 		if(i)
2037 			fs->gd[i].bg_free_inodes_count = nbinodes_per_group;
2038 		else
2039 			fs->gd[i].bg_free_inodes_count = nbinodes_per_group -
2040 							EXT2_FIRST_INO + 2;
2041 		fs->gd[i].bg_used_dirs_count = 0;
2042 		fs->gd[i].bg_block_bitmap = bbmpos;
2043 		fs->gd[i].bg_inode_bitmap = ibmpos;
2044 		fs->gd[i].bg_inode_table = itblpos;
2045 	}
2046 
2047 	/* Mark non-filesystem blocks and inodes as allocated */
2048 	/* Mark system blocks and inodes as allocated         */
2049 	for(i = 0; i<nbgroups;i++) {
2050 
2051 		/* Block bitmap */
2052 		bbm = get_blk(fs,fs->gd[i].bg_block_bitmap);
2053 		//non-filesystem blocks
2054 		for(j = fs->gd[i].bg_free_blocks_count
2055 		        + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++)
2056 			allocate(bbm, j);
2057 		//system blocks
2058 		for(j = 1; j <= overhead_per_group; j++)
2059 			allocate(bbm, j);
2060 
2061 		/* Inode bitmap */
2062 		ibm = get_blk(fs,fs->gd[i].bg_inode_bitmap);
2063 		//non-filesystem inodes
2064 		for(j = fs->sb.s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
2065 			allocate(ibm, j);
2066 
2067 		//system inodes
2068 		if(i == 0)
2069 			for(j = 1; j < EXT2_FIRST_INO; j++)
2070 				allocate(ibm, j);
2071 	}
2072 
2073 	// make root inode and directory
2074 	/* We have groups now. Add the root filesystem in group 0 */
2075 	/* Also increment the directory count for group 0 */
2076 	fs->gd[0].bg_free_inodes_count--;
2077 	fs->gd[0].bg_used_dirs_count = 1;
2078 	itab0 = (inode *)get_blk(fs,fs->gd[0].bg_inode_table);
2079 	itab0[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH;
2080 	itab0[EXT2_ROOT_INO-1].i_ctime = fs_timestamp;
2081 	itab0[EXT2_ROOT_INO-1].i_mtime = fs_timestamp;
2082 	itab0[EXT2_ROOT_INO-1].i_atime = fs_timestamp;
2083 	itab0[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
2084 	itab0[EXT2_ROOT_INO-1].i_links_count = 2;
2085 
2086 	if(!(b = get_workblk()))
2087 		error_msg_and_die("get_workblk() failed.");
2088 	d = (directory*)b;
2089 	d->d_inode = EXT2_ROOT_INO;
2090 	d->d_rec_len = sizeof(directory)+4;
2091 	d->d_name_len = 1;
2092 	strcpy(d->d_name, ".");
2093 	d = (directory*)(b + d->d_rec_len);
2094 	d->d_inode = EXT2_ROOT_INO;
2095 	d->d_rec_len = BLOCKSIZE - (sizeof(directory)+4);
2096 	d->d_name_len = 2;
2097 	strcpy(d->d_name, "..");
2098 	extend_blk(fs, EXT2_ROOT_INO, b, 1);
2099 
2100 	// make lost+found directory and reserve blocks
2101 	if(fs->sb.s_r_blocks_count)
2102 	{
2103 		nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp);
2104 		memset(b, 0, BLOCKSIZE);
2105 		((directory*)b)->d_rec_len = BLOCKSIZE;
2106 		/* We run into problems with e2fsck if directory lost+found grows
2107 		 * bigger than this. Need to find out why this happens - sundar
2108 		 */
2109 		if (fs->sb.s_r_blocks_count > fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS )
2110 			fs->sb.s_r_blocks_count = fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS;
2111 		for(i = 1; i < fs->sb.s_r_blocks_count; i++)
2112 			extend_blk(fs, nod, b, 1);
2113 		get_nod(fs, nod)->i_size = fs->sb.s_r_blocks_count * BLOCKSIZE;
2114 	}
2115 	free_workblk(b);
2116 
2117 	// administrative info
2118 	fs->sb.s_state = 1;
2119 	fs->sb.s_max_mnt_count = 20;
2120 
2121 	// options for me
2122 	if(holes)
2123 		fs->sb.s_reserved[200] |= OP_HOLES;
2124 
2125 	return fs;
2126 }
2127 
2128 // loads a filesystem from disk
2129 static filesystem *
load_fs(FILE * fh,int swapit)2130 load_fs(FILE * fh, int swapit)
2131 {
2132 	size_t fssize;
2133 	filesystem *fs;
2134 	if((fseek(fh, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fh)) == -1))
2135 		perror_msg_and_die("input filesystem image");
2136 	rewind(fh);
2137 	fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
2138 	if(fssize < 16) // totally arbitrary
2139 		error_msg_and_die("too small filesystem");
2140 	if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE)))
2141 		error_msg_and_die("not enough memory for filesystem");
2142 	if(fread(fs, BLOCKSIZE, fssize, fh) != fssize)
2143 		perror_msg_and_die("input filesystem image");
2144 	if(swapit)
2145 		swap_badfs(fs);
2146 	if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER))
2147 		error_msg_and_die("not a suitable ext2 filesystem");
2148 	return fs;
2149 }
2150 
2151 static void
free_fs(filesystem * fs)2152 free_fs(filesystem *fs)
2153 {
2154 	free(fs);
2155 }
2156 
2157 // just walk through blocks list
2158 static void
flist_blocks(filesystem * fs,uint32 nod,FILE * fh)2159 flist_blocks(filesystem *fs, uint32 nod, FILE *fh)
2160 {
2161 	blockwalker bw;
2162 	uint32 bk;
2163 	init_bw(&bw);
2164 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
2165 		fprintf(fh, " %d", bk);
2166 	fprintf(fh, "\n");
2167 }
2168 
2169 // walk through blocks list
2170 static void
list_blocks(filesystem * fs,uint32 nod)2171 list_blocks(filesystem *fs, uint32 nod)
2172 {
2173 	int bn = 0;
2174 	blockwalker bw;
2175 	uint32 bk;
2176 	init_bw(&bw);
2177 	printf("blocks in inode %d:", nod);
2178 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
2179 		printf(" %d", bk), bn++;
2180 	printf("\n%d blocks (%d bytes)\n", bn, bn * BLOCKSIZE);
2181 }
2182 
2183 // saves blocks to FILE*
2184 static void
write_blocks(filesystem * fs,uint32 nod,FILE * f)2185 write_blocks(filesystem *fs, uint32 nod, FILE* f)
2186 {
2187 	blockwalker bw;
2188 	uint32 bk;
2189 	int32 fsize = get_nod(fs, nod)->i_size;
2190 	init_bw(&bw);
2191 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
2192 	{
2193 		if(fsize <= 0)
2194 			error_msg_and_die("wrong size while saving inode %d", nod);
2195 		if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
2196 			error_msg_and_die("error while saving inode %d", nod);
2197 		fsize -= BLOCKSIZE;
2198 	}
2199 }
2200 
2201 
2202 // print block/char device minor and major
2203 static void
print_dev(filesystem * fs,uint32 nod)2204 print_dev(filesystem *fs, uint32 nod)
2205 {
2206 	int minor, major;
2207 	minor = ((uint8*)get_nod(fs, nod)->i_block)[0];
2208 	major = ((uint8*)get_nod(fs, nod)->i_block)[1];
2209 	printf("major: %d, minor: %d\n", major, minor);
2210 }
2211 
2212 // print an inode as a directory
2213 static void
print_dir(filesystem * fs,uint32 nod)2214 print_dir(filesystem *fs, uint32 nod)
2215 {
2216 	blockwalker bw;
2217 	uint32 bk;
2218 	init_bw(&bw);
2219 	printf("directory for inode %d:\n", nod);
2220 	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
2221 	{
2222 		directory *d;
2223 		uint8 *b;
2224 		b = get_blk(fs, bk);
2225 		for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
2226 			if(d->d_inode)
2227 			{
2228 				int i;
2229 				printf("entry '");
2230 				for(i = 0; i < d->d_name_len; i++)
2231 					putchar(d->d_name[i]);
2232 				printf("' (inode %d): rec_len: %d (name_len: %d)\n", d->d_inode, d->d_rec_len, d->d_name_len);
2233 			}
2234 	}
2235 }
2236 
2237 // print a symbolic link
2238 static void
print_link(filesystem * fs,uint32 nod)2239 print_link(filesystem *fs, uint32 nod)
2240 {
2241 	if(!get_nod(fs, nod)->i_blocks)
2242 		printf("links to '%s'\n", (char*)get_nod(fs, nod)->i_block);
2243 	else
2244 	{
2245 		printf("links to '");
2246 		write_blocks(fs, nod, stdout);
2247 		printf("'\n");
2248 	}
2249 }
2250 
2251 // make a ls-like printout of permissions
2252 static void
make_perms(uint32 mode,char perms[11])2253 make_perms(uint32 mode, char perms[11])
2254 {
2255 	strcpy(perms, "----------");
2256 	if(mode & FM_IRUSR)
2257 		perms[1] = 'r';
2258 	if(mode & FM_IWUSR)
2259 		perms[2] = 'w';
2260 	if(mode & FM_IXUSR)
2261 		perms[3] = 'x';
2262 	if(mode & FM_IRGRP)
2263 		perms[4] = 'r';
2264 	if(mode & FM_IWGRP)
2265 		perms[5] = 'w';
2266 	if(mode & FM_IXGRP)
2267 		perms[6] = 'x';
2268 	if(mode & FM_IROTH)
2269 		perms[7] = 'r';
2270 	if(mode & FM_IWOTH)
2271 		perms[8] = 'w';
2272 	if(mode & FM_IXOTH)
2273 		perms[9] = 'x';
2274 	if(mode & FM_ISUID)
2275 		perms[3] = 's';
2276 	if(mode & FM_ISGID)
2277 		perms[6] = 's';
2278 	if(mode & FM_ISVTX)
2279 		perms[9] = 't';
2280 	switch(mode & FM_IFMT)
2281 	{
2282 		case 0:
2283 			*perms = '0';
2284 			break;
2285 		case FM_IFSOCK:
2286 			*perms = 's';
2287 			break;
2288 		case FM_IFLNK:
2289 			*perms = 'l';
2290 			break;
2291 		case FM_IFREG:
2292 			*perms = '-';
2293 			break;
2294 		case FM_IFBLK:
2295 			*perms = 'b';
2296 			break;
2297 		case FM_IFDIR:
2298 			*perms = 'd';
2299 			break;
2300 		case FM_IFCHR:
2301 			*perms = 'c';
2302 			break;
2303 		case FM_IFIFO:
2304 			*perms = 'p';
2305 			break;
2306 		default:
2307 			*perms = '?';
2308 	}
2309 }
2310 
2311 // print an inode
2312 static void
print_inode(filesystem * fs,uint32 nod)2313 print_inode(filesystem *fs, uint32 nod)
2314 {
2315 	char *s;
2316 	char perms[11];
2317 	if(!get_nod(fs, nod)->i_mode)
2318 		return;
2319 	switch(nod)
2320 	{
2321 		case EXT2_BAD_INO:
2322 			s = "bad blocks";
2323 			break;
2324 		case EXT2_ROOT_INO:
2325 			s = "root";
2326 			break;
2327 		case EXT2_ACL_IDX_INO:
2328 		case EXT2_ACL_DATA_INO:
2329 			s = "ACL";
2330 			break;
2331 		case EXT2_BOOT_LOADER_INO:
2332 			s = "boot loader";
2333 			break;
2334 		case EXT2_UNDEL_DIR_INO:
2335 			s = "undelete directory";
2336 			break;
2337 		default:
2338 			s = (nod >= EXT2_FIRST_INO) ? "normal" : "unknown reserved";
2339 	}
2340 	printf("inode %d (%s, %d links): ", nod, s, get_nod(fs, nod)->i_links_count);
2341 	if(!allocated(GRP_GET_INODE_BITMAP(fs,nod), GRP_IBM_OFFSET(fs,nod)))
2342 	{
2343 		printf("unallocated\n");
2344 		return;
2345 	}
2346 	make_perms(get_nod(fs, nod)->i_mode, perms);
2347 	printf("%s,  size: %d byte%s (%d block%s)\n", perms, plural(get_nod(fs, nod)->i_size), plural(get_nod(fs, nod)->i_blocks / INOBLK));
2348 	switch(get_nod(fs, nod)->i_mode & FM_IFMT)
2349 	{
2350 		case FM_IFSOCK:
2351 			list_blocks(fs, nod);
2352 			break;
2353 		case FM_IFLNK:
2354 			print_link(fs, nod);
2355 			break;
2356 		case FM_IFREG:
2357 			list_blocks(fs, nod);
2358 			break;
2359 		case FM_IFBLK:
2360 			print_dev(fs, nod);
2361 			break;
2362 		case FM_IFDIR:
2363 			list_blocks(fs, nod);
2364 			print_dir(fs, nod);
2365 			break;
2366 		case FM_IFCHR:
2367 			print_dev(fs, nod);
2368 			break;
2369 		case FM_IFIFO:
2370 			list_blocks(fs, nod);
2371 			break;
2372 		default:
2373 			list_blocks(fs, nod);
2374 	}
2375 	printf("Done with inode %d\n",nod);
2376 }
2377 
2378 // describes various fields in a filesystem
2379 static void
print_fs(filesystem * fs)2380 print_fs(filesystem *fs)
2381 {
2382 	uint32 i;
2383 	uint8 *ibm;
2384 
2385 	printf("%d blocks (%d free, %d reserved), first data block: %d\n",
2386 	       fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
2387 	       fs->sb.s_r_blocks_count, fs->sb.s_first_data_block);
2388 	printf("%d inodes (%d free)\n", fs->sb.s_inodes_count,
2389 	       fs->sb.s_free_inodes_count);
2390 	printf("block size = %d, frag size = %d\n",
2391 	       fs->sb.s_log_block_size ? (fs->sb.s_log_block_size << 11) : 1024,
2392 	       fs->sb.s_log_frag_size ? (fs->sb.s_log_frag_size << 11) : 1024);
2393 	printf("number of groups: %d\n",GRP_NBGROUPS(fs));
2394 	printf("%d blocks per group,%d frags per group,%d inodes per group\n",
2395 	     fs->sb.s_blocks_per_group, fs->sb.s_frags_per_group,
2396 	     fs->sb.s_inodes_per_group);
2397 	printf("Size of inode table: %d blocks\n",
2398 		(int)(fs->sb.s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
2399 	for (i = 0; i < GRP_NBGROUPS(fs); i++) {
2400 		printf("Group No: %d\n", i+1);
2401 		printf("block bitmap: block %d,inode bitmap: block %d, inode table: block %d\n",
2402 		     fs->gd[i].bg_block_bitmap, fs->gd[i].bg_inode_bitmap,
2403 		     fs->gd[i].bg_inode_table);
2404 		printf("block bitmap allocation:\n");
2405 		print_bm(GRP_GET_GROUP_BBM(fs, i),fs->sb.s_blocks_per_group);
2406 		printf("inode bitmap allocation:\n");
2407 		ibm = GRP_GET_GROUP_IBM(fs, i);
2408 		print_bm(ibm, fs->sb.s_inodes_per_group);
2409 		for (i = 1; i <= fs->sb.s_inodes_per_group; i++)
2410 			if (allocated(ibm, i))
2411 				print_inode(fs, i);
2412 	}
2413 }
2414 
2415 static void
dump_fs(filesystem * fs,FILE * fh,int swapit)2416 dump_fs(filesystem *fs, FILE * fh, int swapit)
2417 {
2418 	uint32 nbblocks = fs->sb.s_blocks_count;
2419 	fs->sb.s_reserved[200] = 0;
2420 	if(swapit)
2421 		swap_goodfs(fs);
2422 	if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
2423 		perror_msg_and_die("output filesystem image");
2424 	if(swapit)
2425 		swap_badfs(fs);
2426 }
2427 
2428 static void
populate_fs(filesystem * fs,char ** dopt,int didx,int squash_uids,int squash_perms,int fixstats,uint32 fs_timestamp,struct stats * stats)2429 populate_fs(filesystem *fs, char **dopt, int didx, int squash_uids, int squash_perms, int fixstats, uint32 fs_timestamp, struct stats *stats)
2430 {
2431 	int i;
2432 	for(i = 0; i < didx; i++)
2433 	{
2434 		struct stat st;
2435 		FILE *fh;
2436 		int pdir;
2437 		char *pdest;
2438 		uint32 nod = EXT2_ROOT_INO;
2439 		if(fs)
2440 			if((pdest = strchr(dopt[i], ':')))
2441 			{
2442 				*(pdest++) = 0;
2443 				if(!(nod = find_path(fs, EXT2_ROOT_INO, pdest)))
2444 					error_msg_and_die("path %s not found in filesystem", pdest);
2445 			}
2446 		stat(dopt[i], &st);
2447 		switch(st.st_mode & S_IFMT)
2448 		{
2449 			case S_IFREG:
2450 				fh = xfopen(dopt[i], "rb");
2451 				add2fs_from_file(fs, nod, fh, fs_timestamp, stats);
2452 				fclose(fh);
2453 				break;
2454 			case S_IFDIR:
2455 				if((pdir = open(".", O_RDONLY)) < 0)
2456 					perror_msg_and_die(".");
2457 				if(chdir(dopt[i]) < 0)
2458 					perror_msg_and_die(dopt[i]);
2459 				if (fixstats)
2460 					prep_stat(dopt[i]);
2461 				add2fs_from_dir(fs, dopt[i], nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
2462 				if(fchdir(pdir) < 0)
2463 					perror_msg_and_die("fchdir");
2464 				if(close(pdir) < 0)
2465 					perror_msg_and_die("close");
2466 				break;
2467 			default:
2468 				error_msg_and_die("%s is neither a file nor a directory", dopt[i]);
2469 		}
2470 	}
2471 }
2472 
2473 static void
showversion(void)2474 showversion(void)
2475 {
2476 	printf("genext2fs " VERSION "\n");
2477 }
2478 
2479 static void
showhelp(void)2480 showhelp(void)
2481 {
2482 	fprintf(stderr, "Usage: %s [options] image\n"
2483 	"Create an ext2 filesystem image from directories/files\n\n"
2484 	"  -x, --starting-image <image>\n"
2485 	"  -d, --root <directory>\n"
2486 	"  -D, --devtable <file>\n"
2487 	"  -b, --size-in-blocks <blocks>\n"
2488 	"  -i, --bytes-per-inode <bytes per inode>\n"
2489 	"  -N, --number-of-inodes <number of inodes>\n"
2490 	"  -m, --reserved-percentage <percentage of blocks to reserve>\n"
2491 	"  -g, --block-map <path>     Generate a block map file for this path.\n"
2492 	"  -e, --fill-value <value>   Fill unallocated blocks with value.\n"
2493 	"  -z, --allow-holes          Allow files with holes.\n"
2494 	"  -f, --faketime             Set filesystem timestamps to 0 (for testing).\n"
2495 	"  -q, --squash               Same as \"-U -P\".\n"
2496 	"  -U, --squash-uids          Squash owners making all files be owned by root.\n"
2497 	"  -P, --squash-perms         Squash permissions on all files.\n"
2498 	"  -a, --fix-android-stats    Fix-up file stats (user, perms, ...)\n"
2499 	"  -h, --help\n"
2500 	"  -V, --version\n"
2501 	"  -v, --verbose\n\n"
2502 	"Report bugs to genext2fs-devel@lists.sourceforge.net\n", app_name);
2503 }
2504 
2505 #define MAX_DOPT 128
2506 #define MAX_GOPT 128
2507 
2508 #define MAX_FILENAME 255
2509 
2510 extern char* optarg;
2511 extern int optind, opterr, optopt;
2512 
2513 int
main(int argc,char ** argv)2514 main(int argc, char **argv)
2515 {
2516 	int nbblocks = -1;
2517 	int nbinodes = -1;
2518 	int nbresrvd = -1;
2519 	float bytes_per_inode = -1;
2520 	float reserved_frac = -1;
2521 	int fs_timestamp = -1;
2522 	char * fsout = "-";
2523 	char * fsin = 0;
2524 	char * dopt[MAX_DOPT];
2525 	int didx = 0;
2526 	char * gopt[MAX_GOPT];
2527 	int gidx = 0;
2528 	int verbose = 0;
2529 	int holes = 0;
2530 	int emptyval = 0;
2531 	int squash_uids = 0;
2532 	int squash_perms = 0;
2533 	int fix_android_stats = 0;
2534 	uint16 endian = 1;
2535 	int bigendian = !*(char*)&endian;
2536 	filesystem *fs;
2537 	int i;
2538 	int c;
2539 	struct stats stats;
2540 
2541 #if HAVE_GETOPT_LONG
2542 	struct option longopts[] = {
2543 	  { "starting-image",	required_argument,	NULL, 'x' },
2544 	  { "root",		required_argument,	NULL, 'd' },
2545 	  { "devtable",		required_argument,	NULL, 'D' },
2546 	  { "size-in-blocks",	required_argument,	NULL, 'b' },
2547 	  { "bytes-per-inode",	required_argument,	NULL, 'i' },
2548 	  { "number-of-inodes",	required_argument,	NULL, 'N' },
2549 	  { "reserved-percentage", required_argument,	NULL, 'm' },
2550 	  { "block-map",	required_argument,	NULL, 'g' },
2551 	  { "fill-value",	required_argument,	NULL, 'e' },
2552 	  { "allow-holes",	no_argument, 		NULL, 'z' },
2553 	  { "faketime",		no_argument,		NULL, 'f' },
2554 	  { "squash",		no_argument,		NULL, 'q' },
2555 	  { "squash-uids",	no_argument,		NULL, 'U' },
2556 	  { "squash-perms",	no_argument,		NULL, 'P' },
2557 	  { "fix-android-stats",no_argument,		NULL, 'a' },
2558 	  { "help",		no_argument,		NULL, 'h' },
2559 	  { "version",		no_argument,		NULL, 'V' },
2560 	  { "verbose",		no_argument,		NULL, 'v' },
2561 	  { 0, 0, 0, 0}
2562 	} ;
2563 
2564 	app_name = argv[0];
2565 
2566 	while((c = getopt_long(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPahVv", longopts, NULL)) != EOF) {
2567 #else
2568 	app_name = argv[0];
2569 
2570 	while((c = getopt(argc, argv,      "x:d:D:b:i:N:m:g:e:zfqUPahVv")) != EOF) {
2571 #endif /* HAVE_GETOPT_LONG */
2572 		switch(c)
2573 		{
2574 			case 'x':
2575 				fsin = optarg;
2576 				break;
2577 			case 'd':
2578 			case 'D':
2579 				dopt[didx++] = optarg;
2580 				break;
2581 			case 'b':
2582 				nbblocks = SI_atof(optarg);
2583 				break;
2584 			case 'i':
2585 				bytes_per_inode = SI_atof(optarg);
2586 				break;
2587 			case 'N':
2588 				nbinodes = SI_atof(optarg);
2589 				break;
2590 			case 'm':
2591 				reserved_frac = SI_atof(optarg) / 100;
2592 				break;
2593 			case 'g':
2594 				gopt[gidx++] = optarg;
2595 				break;
2596 			case 'e':
2597 				emptyval = atoi(optarg);
2598 				break;
2599 			case 'z':
2600 				holes = 1;
2601 				break;
2602 			case 'f':
2603 				fs_timestamp = 0;
2604 				break;
2605 			case 'q':
2606 				squash_uids = 1;
2607 				squash_perms = 1;
2608 				break;
2609 			case 'U':
2610 				squash_uids = 1;
2611 				break;
2612 			case 'P':
2613 				squash_perms = 1;
2614 				break;
2615 			case 'a':
2616 				fix_android_stats = 1;
2617 				break;
2618 			case 'h':
2619 				showhelp();
2620 				exit(0);
2621 			case 'V':
2622 				showversion();
2623 				exit(0);
2624 			case 'v':
2625 				verbose = 1;
2626 				showversion();
2627 				break;
2628 			default:
2629 				error_msg_and_die("Note: options have changed, see --help or the man page.");
2630 		}
2631 	}
2632 
2633 	if(optind < (argc - 1))
2634 		error_msg_and_die("Too many arguments. Try --help or else see the man page.");
2635 	if(optind > (argc - 1))
2636 		error_msg_and_die("Not enough arguments. Try --help or else see the man page.");
2637 
2638 	if(fix_android_stats && (squash_uids || squash_perms))
2639 		error_msg_and_die("Cannot squash uid/perms and fix them up for Android at the same time.");
2640 
2641 	fsout = argv[optind];
2642 
2643 	hdlinks.hdl = (struct hdlink_s *)malloc(hdlink_cnt * sizeof(struct hdlink_s));
2644 	if (!hdlinks.hdl)
2645 		error_msg_and_die("Not enough memory");
2646 	hdlinks.count = 0 ;
2647 
2648 	if(fsin)
2649 	{
2650 		if(strcmp(fsin, "-"))
2651 		{
2652 			FILE * fh = xfopen(fsin, "rb");
2653 			fs = load_fs(fh, bigendian);
2654 			fclose(fh);
2655 		}
2656 		else
2657 			fs = load_fs(stdin, bigendian);
2658 	}
2659 	else
2660 	{
2661 		if(reserved_frac == -1)
2662 			nbresrvd = nbblocks * RESERVED_BLOCKS;
2663 		else
2664 			nbresrvd = nbblocks * reserved_frac;
2665 
2666 		stats.ninodes = EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0);
2667 		stats.nblocks = 0;
2668 
2669 		populate_fs(NULL, dopt, didx, squash_uids, squash_perms, 0, fs_timestamp, &stats);
2670 
2671 		if(nbinodes == -1)
2672 			nbinodes = stats.ninodes;
2673 		else
2674 			if(stats.ninodes > (unsigned long)nbinodes)
2675 			{
2676 				fprintf(stderr, "number of inodes too low, increasing to %ld\n", stats.ninodes);
2677 				nbinodes = stats.ninodes;
2678 			}
2679 
2680 		if(bytes_per_inode != -1) {
2681 			int tmp_nbinodes = nbblocks * BLOCKSIZE / bytes_per_inode;
2682 			if(tmp_nbinodes > nbinodes)
2683 				nbinodes = tmp_nbinodes;
2684 		}
2685 		if(fs_timestamp == -1)
2686 			fs_timestamp = time(NULL);
2687 		fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, fs_timestamp);
2688 	}
2689 
2690 	populate_fs(fs, dopt, didx, squash_uids, squash_perms, fix_android_stats, fs_timestamp, NULL);
2691 
2692 	if(emptyval) {
2693 		uint32 b;
2694 		for(b = 1; b < fs->sb.s_blocks_count; b++)
2695 			if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b),GRP_BBM_OFFSET(fs,b)))
2696 				memset(get_blk(fs, b), emptyval, BLOCKSIZE);
2697 	}
2698 	if(verbose)
2699 		print_fs(fs);
2700 	for(i = 0; i < gidx; i++)
2701 	{
2702 		uint32 nod;
2703 		char fname[MAX_FILENAME];
2704 		char *p;
2705 		FILE *fh;
2706 		if(!(nod = find_path(fs, EXT2_ROOT_INO, gopt[i])))
2707 			error_msg_and_die("path %s not found in filesystem", gopt[i]);
2708 		while((p = strchr(gopt[i], '/')))
2709 			*p = '_';
2710 		SNPRINTF(fname, MAX_FILENAME-1, "%s.blk", gopt[i]);
2711 		fh = xfopen(fname, "wb");
2712 		fprintf(fh, "%d:", get_nod(fs, nod)->i_size);
2713 		flist_blocks(fs, nod, fh);
2714 		fclose(fh);
2715 	}
2716 	if(strcmp(fsout, "-"))
2717 	{
2718 		FILE * fh = xfopen(fsout, "wb");
2719 		dump_fs(fs, fh, bigendian);
2720 		fclose(fh);
2721 	}
2722 	else
2723 		dump_fs(fs, stdout, bigendian);
2724 	free_fs(fs);
2725 	return 0;
2726 }
2727