1 /*
2 * Create a squashfs filesystem. This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
6 * 2012, 2013, 2014
7 * Phillip Lougher <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2,
12 * or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 * mksquashfs.c
24 */
25
26 #define FALSE 0
27 #define TRUE 1
28 #define MAX_LINE 16384
29
30 #include <pwd.h>
31 #include <grp.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <stddef.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <dirent.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <signal.h>
44 #include <setjmp.h>
45 #include <sys/types.h>
46 #include <sys/mman.h>
47 #include <pthread.h>
48 #include <regex.h>
49 #include <fnmatch.h>
50 #include <sys/wait.h>
51 #include <limits.h>
52 #include <ctype.h>
53 #include <sys/sysmacros.h>
54
55 #ifndef FNM_EXTMATCH /* glibc extension */
56 #define FNM_EXTMATCH 0
57 #endif
58
59 #ifndef linux
60 #define __BYTE_ORDER BYTE_ORDER
61 #define __BIG_ENDIAN BIG_ENDIAN
62 #define __LITTLE_ENDIAN LITTLE_ENDIAN
63 #include <sys/sysctl.h>
64 #else
65 #include <endian.h>
66 #include <sys/sysinfo.h>
67 #endif
68
69 #include "squashfs_fs.h"
70 #include "squashfs_swap.h"
71 #include "mksquashfs.h"
72 #include "sort.h"
73 #include "pseudo.h"
74 #include "compressor.h"
75 #include "xattr.h"
76 #include "action.h"
77 #include "error.h"
78 #include "progressbar.h"
79 #include "info.h"
80 #include "caches-queues-lists.h"
81 #include "read_fs.h"
82 #include "restore.h"
83 #include "process_fragments.h"
84
85 /* ANDROID CHANGES START*/
86 #ifdef ANDROID
87 #include "android.h"
88 #include "private/android_filesystem_config.h"
89 #include "private/canned_fs_config.h"
90 #include "private/fs_config.h"
91 int android_config = FALSE;
92 char *context_file = NULL;
93 char *mount_point = NULL;
94 char *target_out_path = NULL;
95 fs_config_func_t fs_config_func = NULL;
96 int compress_thresh_per = 0;
97 int align_4k_blocks = TRUE;
98 FILE *block_map_file = NULL;
99 #endif
100 /* ANDROID CHANGES END */
101
102 int delete = FALSE;
103 int fd;
104 struct squashfs_super_block sBlk;
105
106 /* filesystem flags for building */
107 int comp_opts = FALSE;
108 int no_xattrs = XATTR_DEF;
109 int noX = FALSE;
110 int duplicate_checking = TRUE;
111 int noF = FALSE;
112 int no_fragments = FALSE;
113 int always_use_fragments = FALSE;
114 int noI = FALSE;
115 int noD = FALSE;
116 int silent = TRUE;
117 int exportable = TRUE;
118 int sparse_files = TRUE;
119 int old_exclude = TRUE;
120 int use_regex = FALSE;
121 int nopad = FALSE;
122 int exit_on_error = FALSE;
123
124 long long global_uid = -1, global_gid = -1;
125
126 /* superblock attributes */
127 int block_size = SQUASHFS_FILE_SIZE, block_log;
128 unsigned int id_count = 0;
129 int file_count = 0, sym_count = 0, dev_count = 0, dir_count = 0, fifo_count = 0,
130 sock_count = 0;
131
132 /* ANDROID CHANGES START*/
133 #ifdef ANDROID
134 int whitelisted_count = 0;
135 #endif
136 /* ANDROID CHANGES END */
137
138 /* write position within data section */
139 long long bytes = 0, total_bytes = 0;
140
141 /* in memory directory table - possibly compressed */
142 char *directory_table = NULL;
143 unsigned int directory_bytes = 0, directory_size = 0, total_directory_bytes = 0;
144
145 /* cached directory table */
146 char *directory_data_cache = NULL;
147 unsigned int directory_cache_bytes = 0, directory_cache_size = 0;
148
149 /* in memory inode table - possibly compressed */
150 char *inode_table = NULL;
151 unsigned int inode_bytes = 0, inode_size = 0, total_inode_bytes = 0;
152
153 /* cached inode table */
154 char *data_cache = NULL;
155 unsigned int cache_bytes = 0, cache_size = 0, inode_count = 0;
156
157 /* inode lookup table */
158 squashfs_inode *inode_lookup_table = NULL;
159
160 /* in memory directory data */
161 #define I_COUNT_SIZE 128
162 #define DIR_ENTRIES 32
163 #define INODE_HASH_SIZE 65536
164 #define INODE_HASH_MASK (INODE_HASH_SIZE - 1)
165 #define INODE_HASH(dev, ino) (ino & INODE_HASH_MASK)
166
167 struct cached_dir_index {
168 struct squashfs_dir_index index;
169 char *name;
170 };
171
172 struct directory {
173 unsigned int start_block;
174 unsigned int size;
175 unsigned char *buff;
176 unsigned char *p;
177 unsigned int entry_count;
178 unsigned char *entry_count_p;
179 unsigned int i_count;
180 unsigned int i_size;
181 struct cached_dir_index *index;
182 unsigned char *index_count_p;
183 unsigned int inode_number;
184 };
185
186 struct inode_info *inode_info[INODE_HASH_SIZE];
187
188 /* hash tables used to do fast duplicate searches in duplicate check */
189 struct file_info *dupl[65536];
190 int dup_files = 0;
191
192 /* exclude file handling */
193 /* list of exclude dirs/files */
194 struct exclude_info {
195 dev_t st_dev;
196 ino_t st_ino;
197 };
198
199 #define EXCLUDE_SIZE 8192
200 int exclude = 0;
201 struct exclude_info *exclude_paths = NULL;
202 int old_excluded(char *filename, struct stat *buf);
203
204 struct path_entry {
205 char *name;
206 regex_t *preg;
207 struct pathname *paths;
208 };
209
210 struct pathname {
211 int names;
212 struct path_entry *name;
213 };
214
215 struct pathnames {
216 int count;
217 struct pathname *path[0];
218 };
219 #define PATHS_ALLOC_SIZE 10
220
221 struct pathnames *paths = NULL;
222 struct pathname *path = NULL;
223 struct pathname *stickypath = NULL;
224 int excluded(char *name, struct pathnames *paths, struct pathnames **new);
225
226 int fragments = 0;
227
228 #define FRAG_SIZE 32768
229
230 struct squashfs_fragment_entry *fragment_table = NULL;
231 int fragments_outstanding = 0;
232
233 int fragments_locked = FALSE;
234
235 /* current inode number for directories and non directories */
236 unsigned int inode_no = 1;
237 unsigned int root_inode_number = 0;
238
239 /* list of source dirs/files */
240 int source = 0;
241 char **source_path;
242
243 /* list of root directory entries read from original filesystem */
244 int old_root_entries = 0;
245 struct old_root_entry_info {
246 char *name;
247 struct inode_info inode;
248 };
249 struct old_root_entry_info *old_root_entry;
250
251 /* restore orignal filesystem state if appending to existing filesystem is
252 * cancelled */
253 int appending = FALSE;
254 char *sdata_cache, *sdirectory_data_cache, *sdirectory_compressed;
255
256 long long sbytes, stotal_bytes;
257
258 unsigned int sinode_bytes, scache_bytes, sdirectory_bytes,
259 sdirectory_cache_bytes, sdirectory_compressed_bytes,
260 stotal_inode_bytes, stotal_directory_bytes,
261 sinode_count = 0, sfile_count, ssym_count, sdev_count,
262 sdir_count, sfifo_count, ssock_count, sdup_files;
263 int sfragments;
264 int threads;
265
266 /* flag whether destination file is a block device */
267 int block_device = FALSE;
268
269 /* flag indicating whether files are sorted using sort list(s) */
270 int sorted = FALSE;
271
272 /* save destination file name for deleting on error */
273 char *destination_file = NULL;
274
275 /* recovery file for abnormal exit on appending */
276 char *recovery_file = NULL;
277 int recover = TRUE;
278
279 /* uid/gid mapping tables */
280 #define UGID_ENTRIES 340
281
282 struct ugid_map_entry {
283 unsigned int child_id;
284 unsigned int parent_id;
285 unsigned int length;
286 };
287 struct ugid_map_entry uid_mapping[UGID_ENTRIES], gid_mapping[UGID_ENTRIES];
288 unsigned int uid_map_count = 0, gid_map_count = 0;
289
290
291 struct id *id_hash_table[ID_ENTRIES];
292 struct id *id_table[SQUASHFS_IDS], *sid_table[SQUASHFS_IDS];
293 unsigned int uid_count = 0, guid_count = 0;
294 unsigned int sid_count = 0, suid_count = 0, sguid_count = 0;
295
296 struct cache *reader_buffer, *fragment_buffer, *reserve_cache;
297 struct cache *bwriter_buffer, *fwriter_buffer;
298 struct queue *to_reader, *to_deflate, *to_writer, *from_writer,
299 *to_frag, *locked_fragment, *to_process_frag;
300 struct seq_queue *to_main;
301 pthread_t reader_thread, writer_thread, main_thread;
302 pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread;
303 pthread_t *restore_thread = NULL;
304 pthread_mutex_t fragment_mutex = PTHREAD_MUTEX_INITIALIZER;
305 pthread_mutex_t pos_mutex = PTHREAD_MUTEX_INITIALIZER;
306 pthread_mutex_t dup_mutex = PTHREAD_MUTEX_INITIALIZER;
307
308 /* user options that control parallelisation */
309 int processors = -1;
310 int bwriter_size;
311
312 /* compression operations */
313 struct compressor *comp = NULL;
314 int compressor_opt_parsed = FALSE;
315 void *stream = NULL;
316
317 /* xattr stats */
318 unsigned int xattr_bytes = 0, total_xattr_bytes = 0;
319
320 /* fragment to file mapping used when appending */
321 int append_fragments = 0;
322 struct append_file **file_mapping;
323
324 /* root of the in-core directory structure */
325 struct dir_info *root_dir;
326
327 static char *read_from_disk(long long start, unsigned int avail_bytes);
328 void add_old_root_entry(char *name, squashfs_inode inode, int inode_number,
329 int type);
330 struct file_info *duplicate(long long file_size, long long bytes,
331 unsigned int **block_list, long long *start, struct fragment **fragment,
332 struct file_buffer *file_buffer, int blocks, unsigned short checksum,
333 int checksum_flag);
334 struct dir_info *dir_scan1(char *, char *, struct pathnames *,
335 struct dir_ent *(_readdir)(struct dir_info *), int);
336 void dir_scan2(struct dir_info *dir, struct pseudo *pseudo);
337 void dir_scan3(struct dir_info *dir);
338 void dir_scan4(struct dir_info *dir);
339 void dir_scan5(struct dir_info *dir);
340 void dir_scan6(struct dir_info *dir);
341 void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info);
342 struct file_info *add_non_dup(long long file_size, long long bytes,
343 unsigned int *block_list, long long start, struct fragment *fragment,
344 unsigned short checksum, unsigned short fragment_checksum,
345 int checksum_flag, int checksum_frag_flag);
346 long long generic_write_table(int, void *, int, void *, int);
347 void restorefs();
348 struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth);
349 void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad);
350 unsigned short get_checksum_mem(char *buff, int bytes);
351 void check_usable_phys_mem(int total_mem);
352
353 /* ANDROID CHANGES START*/
354 #ifdef ANDROID
355 static int whitelisted(struct stat *buf);
356 static void add_whitelist_entry(char *filename, struct stat *buf);
357 static int add_whitelist(char *path);
358 static void process_whitelist_file(char *argv);
359
360 #define WHITELIST_SIZE 8192
361 int whitelist = 0;
362
363 struct whitelist_info {
364 dev_t st_dev;
365 ino_t st_ino;
366 };
367 char *whitelist_filename = NULL;
368 struct whitelist_info *whitelist_paths = NULL;
369 #endif
370 /* ANDROID CHANGES END */
371
prep_exit()372 void prep_exit()
373 {
374 if(restore_thread) {
375 if(pthread_self() == *restore_thread) {
376 /*
377 * Recursive failure when trying to restore filesystem!
378 * Nothing to do except to exit, otherwise we'll just
379 * appear to hang. The user should be able to restore
380 * from the recovery file (which is why it was added, in
381 * case of catastrophic failure in Mksquashfs)
382 */
383 exit(1);
384 } else {
385 /* signal the restore thread to restore */
386 pthread_kill(*restore_thread, SIGUSR1);
387 pthread_exit(NULL);
388 }
389 } else if(delete) {
390 if(destination_file && !block_device)
391 unlink(destination_file);
392 } else if(recovery_file)
393 unlink(recovery_file);
394 }
395
396
add_overflow(int a,int b)397 int add_overflow(int a, int b)
398 {
399 return (INT_MAX - a) < b;
400 }
401
402
shift_overflow(int a,int shift)403 int shift_overflow(int a, int shift)
404 {
405 return (INT_MAX >> shift) < a;
406 }
407
408
multiply_overflow(int a,int multiplier)409 int multiply_overflow(int a, int multiplier)
410 {
411 return (INT_MAX / multiplier) < a;
412 }
413
414
multiply_overflowll(long long a,int multiplier)415 int multiply_overflowll(long long a, int multiplier)
416 {
417 return (LLONG_MAX / multiplier) < a;
418 }
419
420
421 #define MKINODE(A) ((squashfs_inode)(((squashfs_inode) inode_bytes << 16) \
422 + (((char *)A) - data_cache)))
423
424
restorefs()425 void restorefs()
426 {
427 ERROR("Exiting - restoring original filesystem!\n\n");
428
429 bytes = sbytes;
430 memcpy(data_cache, sdata_cache, cache_bytes = scache_bytes);
431 memcpy(directory_data_cache, sdirectory_data_cache,
432 sdirectory_cache_bytes);
433 directory_cache_bytes = sdirectory_cache_bytes;
434 inode_bytes = sinode_bytes;
435 directory_bytes = sdirectory_bytes;
436 memcpy(directory_table + directory_bytes, sdirectory_compressed,
437 sdirectory_compressed_bytes);
438 directory_bytes += sdirectory_compressed_bytes;
439 total_bytes = stotal_bytes;
440 total_inode_bytes = stotal_inode_bytes;
441 total_directory_bytes = stotal_directory_bytes;
442 inode_count = sinode_count;
443 file_count = sfile_count;
444 sym_count = ssym_count;
445 dev_count = sdev_count;
446 dir_count = sdir_count;
447 fifo_count = sfifo_count;
448 sock_count = ssock_count;
449 dup_files = sdup_files;
450 fragments = sfragments;
451 id_count = sid_count;
452 restore_xattrs();
453 write_filesystem_tables(&sBlk, nopad);
454 exit(1);
455 }
456
457
sighandler()458 void sighandler()
459 {
460 EXIT_MKSQUASHFS();
461 }
462
463
mangle2(void * strm,char * d,char * s,int size,int block_size,int uncompressed,int data_block)464 int mangle2(void *strm, char *d, char *s, int size,
465 int block_size, int uncompressed, int data_block)
466 {
467 int error, c_byte = 0;
468
469 if(!uncompressed) {
470 c_byte = compressor_compress(comp, strm, d, s, size, block_size,
471 &error);
472 if(c_byte == -1)
473 BAD_ERROR("mangle2:: %s compress failed with error "
474 "code %d\n", comp->name, error);
475 }
476
477 if(c_byte == 0 || c_byte >= size ||
478 (c_byte > (size * ((100.0 - compress_thresh_per) / 100.0)))) {
479 memcpy(d, s, size);
480 return size | (data_block ? SQUASHFS_COMPRESSED_BIT_BLOCK :
481 SQUASHFS_COMPRESSED_BIT);
482 }
483
484 return c_byte;
485 }
486
487
mangle(char * d,char * s,int size,int block_size,int uncompressed,int data_block)488 int mangle(char *d, char *s, int size, int block_size,
489 int uncompressed, int data_block)
490 {
491 return mangle2(stream, d, s, size, block_size, uncompressed,
492 data_block);
493 }
494
495
get_inode(int req_size)496 void *get_inode(int req_size)
497 {
498 int data_space;
499 unsigned short c_byte;
500
501 while(cache_bytes >= SQUASHFS_METADATA_SIZE) {
502 if((inode_size - inode_bytes) <
503 ((SQUASHFS_METADATA_SIZE << 1)) + 2) {
504 void *it = realloc(inode_table, inode_size +
505 (SQUASHFS_METADATA_SIZE << 1) + 2);
506 if(it == NULL)
507 MEM_ERROR();
508 inode_table = it;
509 inode_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
510 }
511
512 c_byte = mangle(inode_table + inode_bytes + BLOCK_OFFSET,
513 data_cache, SQUASHFS_METADATA_SIZE,
514 SQUASHFS_METADATA_SIZE, noI, 0);
515 TRACE("Inode block @ 0x%x, size %d\n", inode_bytes, c_byte);
516 SQUASHFS_SWAP_SHORTS(&c_byte, inode_table + inode_bytes, 1);
517 inode_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
518 total_inode_bytes += SQUASHFS_METADATA_SIZE + BLOCK_OFFSET;
519 memmove(data_cache, data_cache + SQUASHFS_METADATA_SIZE,
520 cache_bytes - SQUASHFS_METADATA_SIZE);
521 cache_bytes -= SQUASHFS_METADATA_SIZE;
522 }
523
524 data_space = (cache_size - cache_bytes);
525 if(data_space < req_size) {
526 int realloc_size = cache_size == 0 ?
527 ((req_size + SQUASHFS_METADATA_SIZE) &
528 ~(SQUASHFS_METADATA_SIZE - 1)) : req_size -
529 data_space;
530
531 void *dc = realloc(data_cache, cache_size +
532 realloc_size);
533 if(dc == NULL)
534 MEM_ERROR();
535 cache_size += realloc_size;
536 data_cache = dc;
537 }
538
539 cache_bytes += req_size;
540
541 return data_cache + cache_bytes - req_size;
542 }
543
544
read_bytes(int fd,void * buff,int bytes)545 int read_bytes(int fd, void *buff, int bytes)
546 {
547 int res, count;
548
549 for(count = 0; count < bytes; count += res) {
550 res = read(fd, buff + count, bytes - count);
551 if(res < 1) {
552 if(res == 0)
553 goto bytes_read;
554 else if(errno != EINTR) {
555 ERROR("Read failed because %s\n",
556 strerror(errno));
557 return -1;
558 } else
559 res = 0;
560 }
561 }
562
563 bytes_read:
564 return count;
565 }
566
567
read_fs_bytes(int fd,long long byte,int bytes,void * buff)568 int read_fs_bytes(int fd, long long byte, int bytes, void *buff)
569 {
570 off_t off = byte;
571 int res = 1;
572
573 TRACE("read_fs_bytes: reading from position 0x%llx, bytes %d\n",
574 byte, bytes);
575
576 pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex);
577 pthread_mutex_lock(&pos_mutex);
578 if(lseek(fd, off, SEEK_SET) == -1) {
579 ERROR("read_fs_bytes: Lseek on destination failed because %s, "
580 "offset=0x%llx\n", strerror(errno), off);
581 res = 0;
582 } else if(read_bytes(fd, buff, bytes) < bytes) {
583 ERROR("Read on destination failed\n");
584 res = 0;
585 }
586
587 pthread_cleanup_pop(1);
588 return res;
589 }
590
591
write_bytes(int fd,void * buff,int bytes)592 int write_bytes(int fd, void *buff, int bytes)
593 {
594 int res, count;
595
596 for(count = 0; count < bytes; count += res) {
597 res = write(fd, buff + count, bytes - count);
598 if(res == -1) {
599 if(errno != EINTR) {
600 ERROR("Write failed because %s\n",
601 strerror(errno));
602 return -1;
603 }
604 res = 0;
605 }
606 }
607
608 return 0;
609 }
610
611
write_destination(int fd,long long byte,int bytes,void * buff)612 void write_destination(int fd, long long byte, int bytes, void *buff)
613 {
614 off_t off = byte;
615
616 pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex);
617 pthread_mutex_lock(&pos_mutex);
618
619 if(lseek(fd, off, SEEK_SET) == -1) {
620 ERROR("write_destination: Lseek on destination "
621 "failed because %s, offset=0x%llx\n", strerror(errno),
622 off);
623 BAD_ERROR("Probably out of space on output %s\n",
624 block_device ? "block device" : "filesystem");
625 }
626
627 if(write_bytes(fd, buff, bytes) == -1)
628 BAD_ERROR("Failed to write to output %s\n",
629 block_device ? "block device" : "filesystem");
630
631 pthread_cleanup_pop(1);
632 }
633
634
write_inodes()635 long long write_inodes()
636 {
637 unsigned short c_byte;
638 int avail_bytes;
639 char *datap = data_cache;
640 long long start_bytes = bytes;
641
642 while(cache_bytes) {
643 if(inode_size - inode_bytes <
644 ((SQUASHFS_METADATA_SIZE << 1) + 2)) {
645 void *it = realloc(inode_table, inode_size +
646 ((SQUASHFS_METADATA_SIZE << 1) + 2));
647 if(it == NULL)
648 MEM_ERROR();
649 inode_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
650 inode_table = it;
651 }
652 avail_bytes = cache_bytes > SQUASHFS_METADATA_SIZE ?
653 SQUASHFS_METADATA_SIZE : cache_bytes;
654 c_byte = mangle(inode_table + inode_bytes + BLOCK_OFFSET, datap,
655 avail_bytes, SQUASHFS_METADATA_SIZE, noI, 0);
656 TRACE("Inode block @ 0x%x, size %d\n", inode_bytes, c_byte);
657 SQUASHFS_SWAP_SHORTS(&c_byte, inode_table + inode_bytes, 1);
658 inode_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
659 total_inode_bytes += avail_bytes + BLOCK_OFFSET;
660 datap += avail_bytes;
661 cache_bytes -= avail_bytes;
662 }
663
664 write_destination(fd, bytes, inode_bytes, inode_table);
665 bytes += inode_bytes;
666
667 return start_bytes;
668 }
669
670
write_directories()671 long long write_directories()
672 {
673 unsigned short c_byte;
674 int avail_bytes;
675 char *directoryp = directory_data_cache;
676 long long start_bytes = bytes;
677
678 while(directory_cache_bytes) {
679 if(directory_size - directory_bytes <
680 ((SQUASHFS_METADATA_SIZE << 1) + 2)) {
681 void *dt = realloc(directory_table,
682 directory_size + ((SQUASHFS_METADATA_SIZE << 1)
683 + 2));
684 if(dt == NULL)
685 MEM_ERROR();
686 directory_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
687 directory_table = dt;
688 }
689 avail_bytes = directory_cache_bytes > SQUASHFS_METADATA_SIZE ?
690 SQUASHFS_METADATA_SIZE : directory_cache_bytes;
691 c_byte = mangle(directory_table + directory_bytes +
692 BLOCK_OFFSET, directoryp, avail_bytes,
693 SQUASHFS_METADATA_SIZE, noI, 0);
694 TRACE("Directory block @ 0x%x, size %d\n", directory_bytes,
695 c_byte);
696 SQUASHFS_SWAP_SHORTS(&c_byte,
697 directory_table + directory_bytes, 1);
698 directory_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) +
699 BLOCK_OFFSET;
700 total_directory_bytes += avail_bytes + BLOCK_OFFSET;
701 directoryp += avail_bytes;
702 directory_cache_bytes -= avail_bytes;
703 }
704 write_destination(fd, bytes, directory_bytes, directory_table);
705 bytes += directory_bytes;
706
707 return start_bytes;
708 }
709
710
write_id_table()711 long long write_id_table()
712 {
713 unsigned int id_bytes = SQUASHFS_ID_BYTES(id_count);
714 unsigned int p[id_count];
715 int i;
716
717 TRACE("write_id_table: ids %d, id_bytes %d\n", id_count, id_bytes);
718 for(i = 0; i < id_count; i++) {
719 TRACE("write_id_table: id index %d, id %d", i, id_table[i]->id);
720 SQUASHFS_SWAP_INTS(&id_table[i]->id, p + i, 1);
721 }
722
723 return generic_write_table(id_bytes, p, 0, NULL, noI);
724 }
725
726
get_id(unsigned int id)727 struct id *get_id(unsigned int id)
728 {
729 int hash = ID_HASH(id);
730 struct id *entry = id_hash_table[hash];
731
732 for(; entry; entry = entry->next)
733 if(entry->id == id)
734 break;
735
736 return entry;
737 }
738
739
create_id(unsigned int id)740 struct id *create_id(unsigned int id)
741 {
742 int hash = ID_HASH(id);
743 struct id *entry = malloc(sizeof(struct id));
744 if(entry == NULL)
745 MEM_ERROR();
746 entry->id = id;
747 entry->index = id_count ++;
748 entry->flags = 0;
749 entry->next = id_hash_table[hash];
750 id_hash_table[hash] = entry;
751 id_table[entry->index] = entry;
752 return entry;
753 }
754
755
resolve_child_ugid(unsigned int * ugid,const struct ugid_map_entry * ugid_mapping,unsigned int ugid_map_count)756 int resolve_child_ugid(unsigned int *ugid,
757 const struct ugid_map_entry *ugid_mapping,
758 unsigned int ugid_map_count)
759 {
760 unsigned int i;
761
762 for (i = 0; i < ugid_map_count; i++) {
763 if (ugid_mapping[i].parent_id <= *ugid &&
764 *ugid <
765 ugid_mapping[i].parent_id + ugid_mapping[i].length) {
766 *ugid = ugid_mapping[i].child_id + *ugid -
767 ugid_mapping[i].parent_id;
768 return 1;
769 }
770 }
771
772 return 0;
773 }
774
775
get_uid(unsigned int uid,int resolve)776 unsigned int get_uid(unsigned int uid, int resolve)
777 {
778 struct id *entry;
779
780 if (resolve && !resolve_child_ugid(&uid, uid_mapping, uid_map_count))
781 BAD_ERROR("uid not found in mapping: %d\n", uid);
782 entry = get_id(uid);
783
784 if(entry == NULL) {
785 if(id_count == SQUASHFS_IDS)
786 BAD_ERROR("Out of uids!\n");
787 entry = create_id(uid);
788 }
789
790 if((entry->flags & ISA_UID) == 0) {
791 entry->flags |= ISA_UID;
792 uid_count ++;
793 }
794
795 return entry->index;
796 }
797
798
get_guid(unsigned int guid,int resolve)799 unsigned int get_guid(unsigned int guid, int resolve)
800 {
801 struct id *entry;
802
803 if (resolve && !resolve_child_ugid(&guid, gid_mapping, gid_map_count))
804 BAD_ERROR("gid not found in mapping: %d\n", guid);
805 entry = get_id(guid);
806
807 if(entry == NULL) {
808 if(id_count == SQUASHFS_IDS)
809 BAD_ERROR("Out of gids!\n");
810 entry = create_id(guid);
811 }
812
813 if((entry->flags & ISA_GID) == 0) {
814 entry->flags |= ISA_GID;
815 guid_count ++;
816 }
817
818 return entry->index;
819 }
820
821
822 #define ALLOC_SIZE 128
823
_pathname(struct dir_ent * dir_ent,char * pathname,int * size)824 char *_pathname(struct dir_ent *dir_ent, char *pathname, int *size)
825 {
826 if(pathname == NULL) {
827 pathname = malloc(ALLOC_SIZE);
828 if(pathname == NULL)
829 MEM_ERROR();
830 }
831
832 for(;;) {
833 int res = snprintf(pathname, *size, "%s/%s",
834 dir_ent->our_dir->pathname,
835 dir_ent->source_name ? : dir_ent->name);
836
837 if(res < 0)
838 BAD_ERROR("snprintf failed in pathname\n");
839 else if(res >= *size) {
840 /*
841 * pathname is too small to contain the result, so
842 * increase it and try again
843 */
844 *size = (res + ALLOC_SIZE) & ~(ALLOC_SIZE - 1);
845 pathname = realloc(pathname, *size);
846 if(pathname == NULL)
847 MEM_ERROR();
848 } else
849 break;
850 }
851
852 return pathname;
853 }
854
855
pathname(struct dir_ent * dir_ent)856 char *pathname(struct dir_ent *dir_ent)
857 {
858 static char *pathname = NULL;
859 static int size = ALLOC_SIZE;
860
861 if (dir_ent->nonstandard_pathname)
862 return dir_ent->nonstandard_pathname;
863
864 return pathname = _pathname(dir_ent, pathname, &size);
865 }
866
867
pathname_reader(struct dir_ent * dir_ent)868 char *pathname_reader(struct dir_ent *dir_ent)
869 {
870 static char *pathname = NULL;
871 static int size = ALLOC_SIZE;
872
873 if (dir_ent->nonstandard_pathname)
874 return dir_ent->nonstandard_pathname;
875
876 return pathname = _pathname(dir_ent, pathname, &size);
877 }
878
879
subpathname(struct dir_ent * dir_ent)880 char *subpathname(struct dir_ent *dir_ent)
881 {
882 static char *subpath = NULL;
883 static int size = ALLOC_SIZE;
884 int res;
885
886 if(subpath == NULL) {
887 subpath = malloc(ALLOC_SIZE);
888 if(subpath == NULL)
889 MEM_ERROR();
890 }
891
892 for(;;) {
893 if(dir_ent->our_dir->subpath[0] != '\0')
894 res = snprintf(subpath, size, "%s/%s",
895 dir_ent->our_dir->subpath, dir_ent->name);
896 else
897 res = snprintf(subpath, size, "/%s", dir_ent->name);
898
899 if(res < 0)
900 BAD_ERROR("snprintf failed in subpathname\n");
901 else if(res >= size) {
902 /*
903 * subpath is too small to contain the result, so
904 * increase it and try again
905 */
906 size = (res + ALLOC_SIZE) & ~(ALLOC_SIZE - 1);
907 subpath = realloc(subpath, size);
908 if(subpath == NULL)
909 MEM_ERROR();
910 } else
911 break;
912 }
913
914 return subpath;
915 }
916
917
get_inode_no(struct inode_info * inode)918 static inline unsigned int get_inode_no(struct inode_info *inode)
919 {
920 return inode->inode_number;
921 }
922
923
get_parent_no(struct dir_info * dir)924 static inline unsigned int get_parent_no(struct dir_info *dir)
925 {
926 return dir->depth ? get_inode_no(dir->dir_ent->inode) : inode_no;
927 }
928
929
930 /* ANDROID CHANGES START*/
931 #ifdef ANDROID
932
933 /* Round up the passed |n| value to the smallest multiple of 4096 greater or
934 * equal than |n| and return the 4K-block number for that value. */
round_up_block(unsigned long long n)935 static unsigned long long round_up_block(unsigned long long n) {
936 const unsigned long long kMapBlockSize = 4096;
937 return (n + kMapBlockSize - 1) / kMapBlockSize;
938 }
939
write_block_map_entry(char * sub_path,unsigned long long start_block,unsigned long long total_size,char * mount_point,FILE * block_map_file)940 static inline void write_block_map_entry(char *sub_path, unsigned long long start_block, unsigned long long total_size,
941 char * mount_point, FILE *block_map_file) {
942 if (block_map_file) {
943 /* We assign each 4K block based on what file the first byte of the block
944 * belongs to. The current file consists of the chunk of bytes in the
945 * interval [start_block, start_block + total_size), (closed on the left end
946 * and open on the right end). We then compute the first block whose first
947 * byte is equal to or greater than start_block as |round_start| and then
948 * the first block whose first byte is *past* this interval, as
949 * |round_end + 1|. This means that the blocks that should be assigned to
950 * the current file are in the interval [round_start, round_end + 1), or
951 * simply [round_start, round_end].
952 */
953 unsigned long long round_start = round_up_block(start_block);
954 unsigned long long round_end = round_up_block(start_block + total_size) - 1;
955 if (round_start && total_size && round_start <= round_end) {
956 fprintf(block_map_file, "/%s", mount_point);
957 if (sub_path[0] != '/') fprintf(block_map_file, "/");
958 if (round_start == round_end)
959 fprintf(block_map_file, "%s %lld\n", sub_path, round_start);
960 else
961 fprintf(block_map_file, "%s %lld-%lld\n", sub_path, round_start, round_end);
962 }
963 }
964 }
965 #endif
966 /* ANDROID CHANGES END */
967
create_inode(squashfs_inode * i_no,struct dir_info * dir_info,struct dir_ent * dir_ent,int type,long long byte_size,long long start_block,unsigned int offset,unsigned int * block_list,struct fragment * fragment,struct directory * dir_in,long long sparse)968 int create_inode(squashfs_inode *i_no, struct dir_info *dir_info,
969 struct dir_ent *dir_ent, int type, long long byte_size,
970 long long start_block, unsigned int offset, unsigned int *block_list,
971 struct fragment *fragment, struct directory *dir_in, long long sparse)
972 {
973 struct stat *buf = &dir_ent->inode->buf;
974 union squashfs_inode_header inode_header;
975 struct squashfs_base_inode_header *base = &inode_header.base;
976 void *inode;
977 char *filename = pathname(dir_ent);
978 int nlink = dir_ent->inode->nlink;
979 int xattr = read_xattrs(dir_ent);
980
981 switch(type) {
982 case SQUASHFS_FILE_TYPE:
983 if(dir_ent->inode->nlink > 1 ||
984 byte_size >= (1LL << 32) ||
985 start_block >= (1LL << 32) ||
986 sparse || IS_XATTR(xattr))
987 type = SQUASHFS_LREG_TYPE;
988 break;
989 case SQUASHFS_DIR_TYPE:
990 if(dir_info->dir_is_ldir || IS_XATTR(xattr))
991 type = SQUASHFS_LDIR_TYPE;
992 break;
993 case SQUASHFS_SYMLINK_TYPE:
994 if(IS_XATTR(xattr))
995 type = SQUASHFS_LSYMLINK_TYPE;
996 break;
997 case SQUASHFS_BLKDEV_TYPE:
998 if(IS_XATTR(xattr))
999 type = SQUASHFS_LBLKDEV_TYPE;
1000 break;
1001 case SQUASHFS_CHRDEV_TYPE:
1002 if(IS_XATTR(xattr))
1003 type = SQUASHFS_LCHRDEV_TYPE;
1004 break;
1005 case SQUASHFS_FIFO_TYPE:
1006 if(IS_XATTR(xattr))
1007 type = SQUASHFS_LFIFO_TYPE;
1008 break;
1009 case SQUASHFS_SOCKET_TYPE:
1010 if(IS_XATTR(xattr))
1011 type = SQUASHFS_LSOCKET_TYPE;
1012 break;
1013 }
1014
1015 base->mode = SQUASHFS_MODE(buf->st_mode);
1016 base->uid = get_uid((unsigned int) global_uid == -1 ?
1017 buf->st_uid : global_uid, 1);
1018 base->inode_type = type;
1019 base->guid = get_guid((unsigned int) global_gid == -1 ?
1020 buf->st_gid : global_gid, 1);
1021 base->mtime = buf->st_mtime;
1022 base->inode_number = get_inode_no(dir_ent->inode);
1023
1024 if(type == SQUASHFS_FILE_TYPE) {
1025 int i;
1026 struct squashfs_reg_inode_header *reg = &inode_header.reg;
1027 size_t off = offsetof(struct squashfs_reg_inode_header, block_list);
1028 /* ANDROID CHANGES START*/
1029 #ifdef ANDROID
1030 unsigned long long total_size = 0;
1031 char *sub_path;
1032 #endif
1033 /* ANDROID CHANGES END */
1034
1035 inode = get_inode(sizeof(*reg) + offset * sizeof(unsigned int));
1036 reg->file_size = byte_size;
1037 reg->start_block = start_block;
1038 reg->fragment = fragment->index;
1039 reg->offset = fragment->offset;
1040 SQUASHFS_SWAP_REG_INODE_HEADER(reg, inode);
1041 SQUASHFS_SWAP_INTS(block_list, inode + off, offset);
1042 TRACE("File inode, file_size %lld, start_block 0x%llx, blocks "
1043 "%d, fragment %d, offset %d, size %d\n", byte_size,
1044 start_block, offset, fragment->index, fragment->offset,
1045 fragment->size);
1046 for(i = 0; i < offset; i++) {
1047 TRACE("Block %d, size %d\n", i, block_list[i]);
1048 total_size += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]);
1049 }
1050 /* ANDROID CHANGES START*/
1051 #ifdef ANDROID
1052 sub_path = subpathname(dir_ent);
1053 if (block_map_file && fragment->index == -1) {
1054 write_block_map_entry(sub_path, start_block, total_size, mount_point, block_map_file);
1055 }
1056 #endif
1057 /* ANDROID CHANGES END */
1058 }
1059 else if(type == SQUASHFS_LREG_TYPE) {
1060 /* ANDROID CHANGES START*/
1061 #ifdef ANDROID
1062 unsigned long long total_size = 0;
1063 char *sub_path;
1064 #endif
1065 /* ANDROID CHANGES END */
1066 int i;
1067 struct squashfs_lreg_inode_header *reg = &inode_header.lreg;
1068 size_t off = offsetof(struct squashfs_lreg_inode_header, block_list);
1069
1070 inode = get_inode(sizeof(*reg) + offset * sizeof(unsigned int));
1071 reg->nlink = nlink;
1072 reg->file_size = byte_size;
1073 reg->start_block = start_block;
1074 reg->fragment = fragment->index;
1075 reg->offset = fragment->offset;
1076 if(sparse && sparse >= byte_size)
1077 sparse = byte_size - 1;
1078 reg->sparse = sparse;
1079 reg->xattr = xattr;
1080 SQUASHFS_SWAP_LREG_INODE_HEADER(reg, inode);
1081 SQUASHFS_SWAP_INTS(block_list, inode + off, offset);
1082 TRACE("Long file inode, file_size %lld, start_block 0x%llx, "
1083 "blocks %d, fragment %d, offset %d, size %d, nlink %d"
1084 "\n", byte_size, start_block, offset, fragment->index,
1085 fragment->offset, fragment->size, nlink);
1086 for(i = 0; i < offset; i++) {
1087 TRACE("Block %d, size %d\n", i, block_list[i]);
1088 total_size += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]);
1089 }
1090 /* ANDROID CHANGES START*/
1091 #ifdef ANDROID
1092 sub_path = subpathname(dir_ent);
1093 if (block_map_file && fragment->index == -1) {
1094 write_block_map_entry(sub_path, start_block, total_size, mount_point, block_map_file);
1095 }
1096 #endif
1097 /* ANDROID CHANGES END */
1098 }
1099 else if(type == SQUASHFS_LDIR_TYPE) {
1100 int i;
1101 unsigned char *p;
1102 struct squashfs_ldir_inode_header *dir = &inode_header.ldir;
1103 struct cached_dir_index *index = dir_in->index;
1104 unsigned int i_count = dir_in->i_count;
1105 unsigned int i_size = dir_in->i_size;
1106
1107 if(byte_size >= 1 << 27)
1108 BAD_ERROR("directory greater than 2^27-1 bytes!\n");
1109
1110 inode = get_inode(sizeof(*dir) + i_size);
1111 dir->inode_type = SQUASHFS_LDIR_TYPE;
1112 dir->nlink = dir_ent->dir->directory_count + 2;
1113 dir->file_size = byte_size;
1114 dir->offset = offset;
1115 dir->start_block = start_block;
1116 dir->i_count = i_count;
1117 dir->parent_inode = get_parent_no(dir_ent->our_dir);
1118 dir->xattr = xattr;
1119
1120 SQUASHFS_SWAP_LDIR_INODE_HEADER(dir, inode);
1121 p = inode + offsetof(struct squashfs_ldir_inode_header, index);
1122 for(i = 0; i < i_count; i++) {
1123 SQUASHFS_SWAP_DIR_INDEX(&index[i].index, p);
1124 p += offsetof(struct squashfs_dir_index, name);
1125 memcpy(p, index[i].name, index[i].index.size + 1);
1126 p += index[i].index.size + 1;
1127 }
1128 TRACE("Long directory inode, file_size %lld, start_block "
1129 "0x%llx, offset 0x%x, nlink %d\n", byte_size,
1130 start_block, offset, dir_ent->dir->directory_count + 2);
1131 }
1132 else if(type == SQUASHFS_DIR_TYPE) {
1133 struct squashfs_dir_inode_header *dir = &inode_header.dir;
1134
1135 inode = get_inode(sizeof(*dir));
1136 dir->nlink = dir_ent->dir->directory_count + 2;
1137 dir->file_size = byte_size;
1138 dir->offset = offset;
1139 dir->start_block = start_block;
1140 dir->parent_inode = get_parent_no(dir_ent->our_dir);
1141 SQUASHFS_SWAP_DIR_INODE_HEADER(dir, inode);
1142 TRACE("Directory inode, file_size %lld, start_block 0x%llx, "
1143 "offset 0x%x, nlink %d\n", byte_size, start_block,
1144 offset, dir_ent->dir->directory_count + 2);
1145 }
1146 else if(type == SQUASHFS_CHRDEV_TYPE || type == SQUASHFS_BLKDEV_TYPE) {
1147 struct squashfs_dev_inode_header *dev = &inode_header.dev;
1148 unsigned int major = major(buf->st_rdev);
1149 unsigned int minor = minor(buf->st_rdev);
1150
1151 if(major > 0xfff) {
1152 ERROR("Major %d out of range in device node %s, "
1153 "truncating to %d\n", major, filename,
1154 major & 0xfff);
1155 major &= 0xfff;
1156 }
1157 if(minor > 0xfffff) {
1158 ERROR("Minor %d out of range in device node %s, "
1159 "truncating to %d\n", minor, filename,
1160 minor & 0xfffff);
1161 minor &= 0xfffff;
1162 }
1163 inode = get_inode(sizeof(*dev));
1164 dev->nlink = nlink;
1165 dev->rdev = (major << 8) | (minor & 0xff) |
1166 ((minor & ~0xff) << 12);
1167 SQUASHFS_SWAP_DEV_INODE_HEADER(dev, inode);
1168 TRACE("Device inode, rdev 0x%x, nlink %d\n", dev->rdev, nlink);
1169 }
1170 else if(type == SQUASHFS_LCHRDEV_TYPE || type == SQUASHFS_LBLKDEV_TYPE) {
1171 struct squashfs_ldev_inode_header *dev = &inode_header.ldev;
1172 unsigned int major = major(buf->st_rdev);
1173 unsigned int minor = minor(buf->st_rdev);
1174
1175 if(major > 0xfff) {
1176 ERROR("Major %d out of range in device node %s, "
1177 "truncating to %d\n", major, filename,
1178 major & 0xfff);
1179 major &= 0xfff;
1180 }
1181 if(minor > 0xfffff) {
1182 ERROR("Minor %d out of range in device node %s, "
1183 "truncating to %d\n", minor, filename,
1184 minor & 0xfffff);
1185 minor &= 0xfffff;
1186 }
1187 inode = get_inode(sizeof(*dev));
1188 dev->nlink = nlink;
1189 dev->rdev = (major << 8) | (minor & 0xff) |
1190 ((minor & ~0xff) << 12);
1191 dev->xattr = xattr;
1192 SQUASHFS_SWAP_LDEV_INODE_HEADER(dev, inode);
1193 TRACE("Device inode, rdev 0x%x, nlink %d\n", dev->rdev, nlink);
1194 }
1195 else if(type == SQUASHFS_SYMLINK_TYPE) {
1196 struct squashfs_symlink_inode_header *symlink = &inode_header.symlink;
1197 int byte = strlen(dir_ent->inode->symlink);
1198 size_t off = offsetof(struct squashfs_symlink_inode_header, symlink);
1199
1200 inode = get_inode(sizeof(*symlink) + byte);
1201 symlink->nlink = nlink;
1202 symlink->symlink_size = byte;
1203 SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode);
1204 strncpy(inode + off, dir_ent->inode->symlink, byte);
1205 TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte,
1206 nlink);
1207 }
1208 else if(type == SQUASHFS_LSYMLINK_TYPE) {
1209 struct squashfs_symlink_inode_header *symlink = &inode_header.symlink;
1210 int byte = strlen(dir_ent->inode->symlink);
1211 size_t off = offsetof(struct squashfs_symlink_inode_header, symlink);
1212
1213 inode = get_inode(sizeof(*symlink) + byte +
1214 sizeof(unsigned int));
1215 symlink->nlink = nlink;
1216 symlink->symlink_size = byte;
1217 SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode);
1218 strncpy(inode + off, dir_ent->inode->symlink, byte);
1219 SQUASHFS_SWAP_INTS(&xattr, inode + off + byte, 1);
1220 TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte,
1221 nlink);
1222 }
1223 else if(type == SQUASHFS_FIFO_TYPE || type == SQUASHFS_SOCKET_TYPE) {
1224 struct squashfs_ipc_inode_header *ipc = &inode_header.ipc;
1225
1226 inode = get_inode(sizeof(*ipc));
1227 ipc->nlink = nlink;
1228 SQUASHFS_SWAP_IPC_INODE_HEADER(ipc, inode);
1229 TRACE("ipc inode, type %s, nlink %d\n", type ==
1230 SQUASHFS_FIFO_TYPE ? "fifo" : "socket", nlink);
1231 }
1232 else if(type == SQUASHFS_LFIFO_TYPE || type == SQUASHFS_LSOCKET_TYPE) {
1233 struct squashfs_lipc_inode_header *ipc = &inode_header.lipc;
1234
1235 inode = get_inode(sizeof(*ipc));
1236 ipc->nlink = nlink;
1237 ipc->xattr = xattr;
1238 SQUASHFS_SWAP_LIPC_INODE_HEADER(ipc, inode);
1239 TRACE("ipc inode, type %s, nlink %d\n", type ==
1240 SQUASHFS_FIFO_TYPE ? "fifo" : "socket", nlink);
1241 } else
1242 BAD_ERROR("Unrecognised inode %d in create_inode\n", type);
1243
1244 *i_no = MKINODE(inode);
1245 inode_count ++;
1246
1247 TRACE("Created inode 0x%llx, type %d, uid %d, guid %d\n", *i_no, type,
1248 base->uid, base->guid);
1249
1250 return TRUE;
1251 }
1252
1253
add_dir(squashfs_inode inode,unsigned int inode_number,char * name,int type,struct directory * dir)1254 void add_dir(squashfs_inode inode, unsigned int inode_number, char *name,
1255 int type, struct directory *dir)
1256 {
1257 unsigned char *buff;
1258 struct squashfs_dir_entry idir;
1259 unsigned int start_block = inode >> 16;
1260 unsigned int offset = inode & 0xffff;
1261 unsigned int size = strlen(name);
1262 size_t name_off = offsetof(struct squashfs_dir_entry, name);
1263
1264 if(size > SQUASHFS_NAME_LEN) {
1265 size = SQUASHFS_NAME_LEN;
1266 ERROR("Filename is greater than %d characters, truncating! ..."
1267 "\n", SQUASHFS_NAME_LEN);
1268 }
1269
1270 if(dir->p + sizeof(struct squashfs_dir_entry) + size +
1271 sizeof(struct squashfs_dir_header)
1272 >= dir->buff + dir->size) {
1273 buff = realloc(dir->buff, dir->size += SQUASHFS_METADATA_SIZE);
1274 if(buff == NULL)
1275 MEM_ERROR();
1276
1277 dir->p = (dir->p - dir->buff) + buff;
1278 if(dir->entry_count_p)
1279 dir->entry_count_p = (dir->entry_count_p - dir->buff +
1280 buff);
1281 dir->index_count_p = dir->index_count_p - dir->buff + buff;
1282 dir->buff = buff;
1283 }
1284
1285 if(dir->entry_count == 256 || start_block != dir->start_block ||
1286 ((dir->entry_count_p != NULL) &&
1287 ((dir->p + sizeof(struct squashfs_dir_entry) + size -
1288 dir->index_count_p) > SQUASHFS_METADATA_SIZE)) ||
1289 ((long long) inode_number - dir->inode_number) > 32767
1290 || ((long long) inode_number - dir->inode_number)
1291 < -32768) {
1292 if(dir->entry_count_p) {
1293 struct squashfs_dir_header dir_header;
1294
1295 if((dir->p + sizeof(struct squashfs_dir_entry) + size -
1296 dir->index_count_p) >
1297 SQUASHFS_METADATA_SIZE) {
1298 if(dir->i_count % I_COUNT_SIZE == 0) {
1299 dir->index = realloc(dir->index,
1300 (dir->i_count + I_COUNT_SIZE) *
1301 sizeof(struct cached_dir_index));
1302 if(dir->index == NULL)
1303 MEM_ERROR();
1304 }
1305 dir->index[dir->i_count].index.index =
1306 dir->p - dir->buff;
1307 dir->index[dir->i_count].index.size = size - 1;
1308 dir->index[dir->i_count++].name = name;
1309 dir->i_size += sizeof(struct squashfs_dir_index)
1310 + size;
1311 dir->index_count_p = dir->p;
1312 }
1313
1314 dir_header.count = dir->entry_count - 1;
1315 dir_header.start_block = dir->start_block;
1316 dir_header.inode_number = dir->inode_number;
1317 SQUASHFS_SWAP_DIR_HEADER(&dir_header,
1318 dir->entry_count_p);
1319
1320 }
1321
1322
1323 dir->entry_count_p = dir->p;
1324 dir->start_block = start_block;
1325 dir->entry_count = 0;
1326 dir->inode_number = inode_number;
1327 dir->p += sizeof(struct squashfs_dir_header);
1328 }
1329
1330 idir.offset = offset;
1331 idir.type = type;
1332 idir.size = size - 1;
1333 idir.inode_number = ((long long) inode_number - dir->inode_number);
1334 SQUASHFS_SWAP_DIR_ENTRY(&idir, dir->p);
1335 strncpy((char *) dir->p + name_off, name, size);
1336 dir->p += sizeof(struct squashfs_dir_entry) + size;
1337 dir->entry_count ++;
1338 }
1339
1340
write_dir(squashfs_inode * inode,struct dir_info * dir_info,struct directory * dir)1341 void write_dir(squashfs_inode *inode, struct dir_info *dir_info,
1342 struct directory *dir)
1343 {
1344 unsigned int dir_size = dir->p - dir->buff;
1345 int data_space = directory_cache_size - directory_cache_bytes;
1346 unsigned int directory_block, directory_offset, i_count, index;
1347 unsigned short c_byte;
1348
1349 if(data_space < dir_size) {
1350 int realloc_size = directory_cache_size == 0 ?
1351 ((dir_size + SQUASHFS_METADATA_SIZE) &
1352 ~(SQUASHFS_METADATA_SIZE - 1)) : dir_size - data_space;
1353
1354 void *dc = realloc(directory_data_cache,
1355 directory_cache_size + realloc_size);
1356 if(dc == NULL)
1357 MEM_ERROR();
1358 directory_cache_size += realloc_size;
1359 directory_data_cache = dc;
1360 }
1361
1362 if(dir_size) {
1363 struct squashfs_dir_header dir_header;
1364
1365 dir_header.count = dir->entry_count - 1;
1366 dir_header.start_block = dir->start_block;
1367 dir_header.inode_number = dir->inode_number;
1368 SQUASHFS_SWAP_DIR_HEADER(&dir_header, dir->entry_count_p);
1369 memcpy(directory_data_cache + directory_cache_bytes, dir->buff,
1370 dir_size);
1371 }
1372 directory_offset = directory_cache_bytes;
1373 directory_block = directory_bytes;
1374 directory_cache_bytes += dir_size;
1375 i_count = 0;
1376 index = SQUASHFS_METADATA_SIZE - directory_offset;
1377
1378 while(1) {
1379 while(i_count < dir->i_count &&
1380 dir->index[i_count].index.index < index)
1381 dir->index[i_count++].index.start_block =
1382 directory_bytes;
1383 index += SQUASHFS_METADATA_SIZE;
1384
1385 if(directory_cache_bytes < SQUASHFS_METADATA_SIZE)
1386 break;
1387
1388 if((directory_size - directory_bytes) <
1389 ((SQUASHFS_METADATA_SIZE << 1) + 2)) {
1390 void *dt = realloc(directory_table,
1391 directory_size + (SQUASHFS_METADATA_SIZE << 1)
1392 + 2);
1393 if(dt == NULL)
1394 MEM_ERROR();
1395 directory_size += SQUASHFS_METADATA_SIZE << 1;
1396 directory_table = dt;
1397 }
1398
1399 c_byte = mangle(directory_table + directory_bytes +
1400 BLOCK_OFFSET, directory_data_cache,
1401 SQUASHFS_METADATA_SIZE, SQUASHFS_METADATA_SIZE,
1402 noI, 0);
1403 TRACE("Directory block @ 0x%x, size %d\n", directory_bytes,
1404 c_byte);
1405 SQUASHFS_SWAP_SHORTS(&c_byte,
1406 directory_table + directory_bytes, 1);
1407 directory_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) +
1408 BLOCK_OFFSET;
1409 total_directory_bytes += SQUASHFS_METADATA_SIZE + BLOCK_OFFSET;
1410 memmove(directory_data_cache, directory_data_cache +
1411 SQUASHFS_METADATA_SIZE, directory_cache_bytes -
1412 SQUASHFS_METADATA_SIZE);
1413 directory_cache_bytes -= SQUASHFS_METADATA_SIZE;
1414 }
1415
1416 create_inode(inode, dir_info, dir_info->dir_ent, SQUASHFS_DIR_TYPE,
1417 dir_size + 3, directory_block, directory_offset, NULL, NULL,
1418 dir, 0);
1419
1420 #ifdef SQUASHFS_TRACE
1421 {
1422 unsigned char *dirp;
1423 int count;
1424
1425 TRACE("Directory contents of inode 0x%llx\n", *inode);
1426 dirp = dir->buff;
1427 while(dirp < dir->p) {
1428 char buffer[SQUASHFS_NAME_LEN + 1];
1429 struct squashfs_dir_entry idir, *idirp;
1430 struct squashfs_dir_header dirh;
1431 SQUASHFS_SWAP_DIR_HEADER((struct squashfs_dir_header *) dirp,
1432 &dirh);
1433 count = dirh.count + 1;
1434 dirp += sizeof(struct squashfs_dir_header);
1435
1436 TRACE("\tStart block 0x%x, count %d\n",
1437 dirh.start_block, count);
1438
1439 while(count--) {
1440 idirp = (struct squashfs_dir_entry *) dirp;
1441 SQUASHFS_SWAP_DIR_ENTRY(idirp, &idir);
1442 strncpy(buffer, idirp->name, idir.size + 1);
1443 buffer[idir.size + 1] = '\0';
1444 TRACE("\t\tname %s, inode offset 0x%x, type "
1445 "%d\n", buffer, idir.offset, idir.type);
1446 dirp += sizeof(struct squashfs_dir_entry) + idir.size +
1447 1;
1448 }
1449 }
1450 }
1451 #endif
1452 dir_count ++;
1453 }
1454
1455
get_fragment(struct fragment * fragment)1456 static struct file_buffer *get_fragment(struct fragment *fragment)
1457 {
1458 struct squashfs_fragment_entry *disk_fragment;
1459 struct file_buffer *buffer, *compressed_buffer;
1460 long long start_block;
1461 int res, size, index = fragment->index;
1462 char locked;
1463
1464 /*
1465 * Lookup fragment block in cache.
1466 * If the fragment block doesn't exist, then get the compressed version
1467 * from the writer cache or off disk, and decompress it.
1468 *
1469 * This routine has two things which complicate the code:
1470 *
1471 * 1. Multiple threads can simultaneously lookup/create the
1472 * same buffer. This means a buffer needs to be "locked"
1473 * when it is being filled in, to prevent other threads from
1474 * using it when it is not ready. This is because we now do
1475 * fragment duplicate checking in parallel.
1476 * 2. We have two caches which need to be checked for the
1477 * presence of fragment blocks: the normal fragment cache
1478 * and a "reserve" cache. The reserve cache is used to
1479 * prevent an unnecessary pipeline stall when the fragment cache
1480 * is full of fragments waiting to be compressed.
1481 */
1482
1483 if(fragment->index == SQUASHFS_INVALID_FRAG)
1484 return NULL;
1485
1486 pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
1487 pthread_mutex_lock(&dup_mutex);
1488
1489 again:
1490 buffer = cache_lookup_nowait(fragment_buffer, index, &locked);
1491 if(buffer) {
1492 pthread_mutex_unlock(&dup_mutex);
1493 if(locked)
1494 /* got a buffer being filled in. Wait for it */
1495 cache_wait_unlock(buffer);
1496 goto finished;
1497 }
1498
1499 /* not in fragment cache, is it in the reserve cache? */
1500 buffer = cache_lookup_nowait(reserve_cache, index, &locked);
1501 if(buffer) {
1502 pthread_mutex_unlock(&dup_mutex);
1503 if(locked)
1504 /* got a buffer being filled in. Wait for it */
1505 cache_wait_unlock(buffer);
1506 goto finished;
1507 }
1508
1509 /* in neither cache, try to get it from the fragment cache */
1510 buffer = cache_get_nowait(fragment_buffer, index);
1511 if(!buffer) {
1512 /*
1513 * no room, get it from the reserve cache, this is
1514 * dimensioned so it will always have space (no more than
1515 * processors + 1 can have an outstanding reserve buffer)
1516 */
1517 buffer = cache_get_nowait(reserve_cache, index);
1518 if(!buffer) {
1519 /* failsafe */
1520 ERROR("no space in reserve cache\n");
1521 goto again;
1522 }
1523 }
1524
1525 pthread_mutex_unlock(&dup_mutex);
1526
1527 compressed_buffer = cache_lookup(fwriter_buffer, index);
1528
1529 pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
1530 pthread_mutex_lock(&fragment_mutex);
1531 disk_fragment = &fragment_table[index];
1532 size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size);
1533 start_block = disk_fragment->start_block;
1534 pthread_cleanup_pop(1);
1535
1536 if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) {
1537 int error;
1538 char *data;
1539
1540 if(compressed_buffer)
1541 data = compressed_buffer->data;
1542 else {
1543 data = read_from_disk(start_block, size);
1544 if(data == NULL) {
1545 ERROR("Failed to read fragment from output"
1546 " filesystem\n");
1547 BAD_ERROR("Output filesystem corrupted?\n");
1548 }
1549 }
1550
1551 res = compressor_uncompress(comp, buffer->data, data, size,
1552 block_size, &error);
1553 if(res == -1)
1554 BAD_ERROR("%s uncompress failed with error code %d\n",
1555 comp->name, error);
1556 } else if(compressed_buffer)
1557 memcpy(buffer->data, compressed_buffer->data, size);
1558 else {
1559 res = read_fs_bytes(fd, start_block, size, buffer->data);
1560 if(res == 0) {
1561 ERROR("Failed to read fragment from output "
1562 "filesystem\n");
1563 BAD_ERROR("Output filesystem corrupted?\n");
1564 }
1565 }
1566
1567 cache_unlock(buffer);
1568 cache_block_put(compressed_buffer);
1569
1570 finished:
1571 pthread_cleanup_pop(0);
1572
1573 return buffer;
1574 }
1575
1576
get_fragment_checksum(struct file_info * file)1577 unsigned short get_fragment_checksum(struct file_info *file)
1578 {
1579 struct file_buffer *frag_buffer;
1580 struct append_file *append;
1581 int res, index = file->fragment->index;
1582 unsigned short checksum;
1583
1584 if(index == SQUASHFS_INVALID_FRAG)
1585 return 0;
1586
1587 pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
1588 pthread_mutex_lock(&dup_mutex);
1589 res = file->have_frag_checksum;
1590 checksum = file->fragment_checksum;
1591 pthread_cleanup_pop(1);
1592
1593 if(res)
1594 return checksum;
1595
1596 frag_buffer = get_fragment(file->fragment);
1597
1598 pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
1599
1600 for(append = file_mapping[index]; append; append = append->next) {
1601 int offset = append->file->fragment->offset;
1602 int size = append->file->fragment->size;
1603 unsigned short cksum =
1604 get_checksum_mem(frag_buffer->data + offset, size);
1605
1606 if(file == append->file)
1607 checksum = cksum;
1608
1609 pthread_mutex_lock(&dup_mutex);
1610 append->file->fragment_checksum = cksum;
1611 append->file->have_frag_checksum = TRUE;
1612 pthread_mutex_unlock(&dup_mutex);
1613 }
1614
1615 cache_block_put(frag_buffer);
1616 pthread_cleanup_pop(0);
1617
1618 return checksum;
1619 }
1620
1621
lock_fragments()1622 void lock_fragments()
1623 {
1624 pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
1625 pthread_mutex_lock(&fragment_mutex);
1626 fragments_locked = TRUE;
1627 pthread_cleanup_pop(1);
1628 }
1629
1630
unlock_fragments()1631 void unlock_fragments()
1632 {
1633 int frg, size;
1634 struct file_buffer *write_buffer;
1635
1636 pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
1637 pthread_mutex_lock(&fragment_mutex);
1638
1639 /*
1640 * Note queue_empty() is inherently racy with respect to concurrent
1641 * queue get and pushes. We avoid this because we're holding the
1642 * fragment_mutex which ensures no other threads can be using the
1643 * queue at this time.
1644 */
1645 while(!queue_empty(locked_fragment)) {
1646 write_buffer = queue_get(locked_fragment);
1647 frg = write_buffer->block;
1648 size = SQUASHFS_COMPRESSED_SIZE_BLOCK(fragment_table[frg].size);
1649 fragment_table[frg].start_block = bytes;
1650 write_buffer->block = bytes;
1651 bytes += size;
1652 fragments_outstanding --;
1653 queue_put(to_writer, write_buffer);
1654 TRACE("fragment_locked writing fragment %d, compressed size %d"
1655 "\n", frg, size);
1656 }
1657 fragments_locked = FALSE;
1658 pthread_cleanup_pop(1);
1659 }
1660
1661 /* Called with the fragment_mutex locked */
add_pending_fragment(struct file_buffer * write_buffer,int c_byte,int fragment)1662 void add_pending_fragment(struct file_buffer *write_buffer, int c_byte,
1663 int fragment)
1664 {
1665 fragment_table[fragment].size = c_byte;
1666 write_buffer->block = fragment;
1667
1668 queue_put(locked_fragment, write_buffer);
1669 }
1670
1671
write_fragment(struct file_buffer * fragment)1672 void write_fragment(struct file_buffer *fragment)
1673 {
1674 if(fragment == NULL)
1675 return;
1676
1677 pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
1678 pthread_mutex_lock(&fragment_mutex);
1679 fragment_table[fragment->block].unused = 0;
1680 fragments_outstanding ++;
1681 queue_put(to_frag, fragment);
1682 pthread_cleanup_pop(1);
1683 }
1684
1685
allocate_fragment()1686 struct file_buffer *allocate_fragment()
1687 {
1688 struct file_buffer *fragment = cache_get(fragment_buffer, fragments);
1689
1690 pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
1691 pthread_mutex_lock(&fragment_mutex);
1692
1693 if(fragments % FRAG_SIZE == 0) {
1694 void *ft = realloc(fragment_table, (fragments +
1695 FRAG_SIZE) * sizeof(struct squashfs_fragment_entry));
1696 if(ft == NULL)
1697 MEM_ERROR();
1698 fragment_table = ft;
1699 }
1700
1701 fragment->size = 0;
1702 fragment->block = fragments ++;
1703
1704 pthread_cleanup_pop(1);
1705
1706 return fragment;
1707 }
1708
1709
1710 static struct fragment empty_fragment = {SQUASHFS_INVALID_FRAG, 0, 0};
1711
1712
free_fragment(struct fragment * fragment)1713 void free_fragment(struct fragment *fragment)
1714 {
1715 if(fragment != &empty_fragment)
1716 free(fragment);
1717 }
1718
1719
get_and_fill_fragment(struct file_buffer * file_buffer,struct dir_ent * dir_ent)1720 struct fragment *get_and_fill_fragment(struct file_buffer *file_buffer,
1721 struct dir_ent *dir_ent)
1722 {
1723 struct fragment *ffrg;
1724 struct file_buffer **fragment;
1725
1726 if(file_buffer == NULL || file_buffer->size == 0)
1727 return &empty_fragment;
1728
1729 fragment = eval_frag_actions(root_dir, dir_ent);
1730
1731 if((*fragment) && (*fragment)->size + file_buffer->size > block_size) {
1732 write_fragment(*fragment);
1733 *fragment = NULL;
1734 }
1735
1736 ffrg = malloc(sizeof(struct fragment));
1737 if(ffrg == NULL)
1738 MEM_ERROR();
1739
1740 if(*fragment == NULL)
1741 *fragment = allocate_fragment();
1742
1743 ffrg->index = (*fragment)->block;
1744 ffrg->offset = (*fragment)->size;
1745 ffrg->size = file_buffer->size;
1746 memcpy((*fragment)->data + (*fragment)->size, file_buffer->data,
1747 file_buffer->size);
1748 (*fragment)->size += file_buffer->size;
1749
1750 return ffrg;
1751 }
1752
1753
generic_write_table(int length,void * buffer,int length2,void * buffer2,int uncompressed)1754 long long generic_write_table(int length, void *buffer, int length2,
1755 void *buffer2, int uncompressed)
1756 {
1757 int meta_blocks = (length + SQUASHFS_METADATA_SIZE - 1) /
1758 SQUASHFS_METADATA_SIZE;
1759 long long *list, start_bytes;
1760 int compressed_size, i, list_size = meta_blocks * sizeof(long long);
1761 unsigned short c_byte;
1762 char cbuffer[(SQUASHFS_METADATA_SIZE << 2) + 2];
1763
1764 #ifdef SQUASHFS_TRACE
1765 long long obytes = bytes;
1766 int olength = length;
1767 #endif
1768
1769 list = malloc(list_size);
1770 if(list == NULL)
1771 MEM_ERROR();
1772
1773 for(i = 0; i < meta_blocks; i++) {
1774 int avail_bytes = length > SQUASHFS_METADATA_SIZE ?
1775 SQUASHFS_METADATA_SIZE : length;
1776 c_byte = mangle(cbuffer + BLOCK_OFFSET, buffer + i *
1777 SQUASHFS_METADATA_SIZE , avail_bytes,
1778 SQUASHFS_METADATA_SIZE, uncompressed, 0);
1779 SQUASHFS_SWAP_SHORTS(&c_byte, cbuffer, 1);
1780 list[i] = bytes;
1781 compressed_size = SQUASHFS_COMPRESSED_SIZE(c_byte) +
1782 BLOCK_OFFSET;
1783 TRACE("block %d @ 0x%llx, compressed size %d\n", i, bytes,
1784 compressed_size);
1785 write_destination(fd, bytes, compressed_size, cbuffer);
1786 bytes += compressed_size;
1787 total_bytes += avail_bytes;
1788 length -= avail_bytes;
1789 }
1790
1791 start_bytes = bytes;
1792 if(length2) {
1793 write_destination(fd, bytes, length2, buffer2);
1794 bytes += length2;
1795 total_bytes += length2;
1796 }
1797
1798 SQUASHFS_INSWAP_LONG_LONGS(list, meta_blocks);
1799 write_destination(fd, bytes, list_size, list);
1800 bytes += list_size;
1801 total_bytes += list_size;
1802
1803 TRACE("generic_write_table: total uncompressed %d compressed %lld\n",
1804 olength, bytes - obytes);
1805
1806 free(list);
1807
1808 return start_bytes;
1809 }
1810
1811
write_fragment_table()1812 long long write_fragment_table()
1813 {
1814 unsigned int frag_bytes = SQUASHFS_FRAGMENT_BYTES(fragments);
1815 int i;
1816
1817 TRACE("write_fragment_table: fragments %d, frag_bytes %d\n", fragments,
1818 frag_bytes);
1819 for(i = 0; i < fragments; i++) {
1820 TRACE("write_fragment_table: fragment %d, start_block 0x%llx, "
1821 "size %d\n", i, fragment_table[i].start_block,
1822 fragment_table[i].size);
1823 SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
1824 }
1825
1826 return generic_write_table(frag_bytes, fragment_table, 0, NULL, noF);
1827 }
1828
1829
1830 char read_from_file_buffer[SQUASHFS_FILE_MAX_SIZE];
read_from_disk(long long start,unsigned int avail_bytes)1831 static char *read_from_disk(long long start, unsigned int avail_bytes)
1832 {
1833 int res;
1834
1835 res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer);
1836 if(res == 0)
1837 return NULL;
1838
1839 return read_from_file_buffer;
1840 }
1841
1842
1843 char read_from_file_buffer2[SQUASHFS_FILE_MAX_SIZE];
read_from_disk2(long long start,unsigned int avail_bytes)1844 char *read_from_disk2(long long start, unsigned int avail_bytes)
1845 {
1846 int res;
1847
1848 res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer2);
1849 if(res == 0)
1850 return NULL;
1851
1852 return read_from_file_buffer2;
1853 }
1854
1855
1856 /*
1857 * Compute 16 bit BSD checksum over the data
1858 */
get_checksum(char * buff,int bytes,unsigned short chksum)1859 unsigned short get_checksum(char *buff, int bytes, unsigned short chksum)
1860 {
1861 unsigned char *b = (unsigned char *) buff;
1862
1863 while(bytes --) {
1864 chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1;
1865 chksum += *b++;
1866 }
1867
1868 return chksum;
1869 }
1870
1871
get_checksum_disk(long long start,long long l,unsigned int * blocks)1872 unsigned short get_checksum_disk(long long start, long long l,
1873 unsigned int *blocks)
1874 {
1875 unsigned short chksum = 0;
1876 unsigned int bytes;
1877 struct file_buffer *write_buffer;
1878 int i;
1879
1880 for(i = 0; l; i++) {
1881 bytes = SQUASHFS_COMPRESSED_SIZE_BLOCK(blocks[i]);
1882 if(bytes == 0) /* sparse block */
1883 continue;
1884 write_buffer = cache_lookup(bwriter_buffer, start);
1885 if(write_buffer) {
1886 chksum = get_checksum(write_buffer->data, bytes,
1887 chksum);
1888 cache_block_put(write_buffer);
1889 } else {
1890 void *data = read_from_disk(start, bytes);
1891 if(data == NULL) {
1892 ERROR("Failed to checksum data from output"
1893 " filesystem\n");
1894 BAD_ERROR("Output filesystem corrupted?\n");
1895 }
1896
1897 chksum = get_checksum(data, bytes, chksum);
1898 }
1899
1900 l -= bytes;
1901 start += bytes;
1902 }
1903
1904 return chksum;
1905 }
1906
1907
get_checksum_mem(char * buff,int bytes)1908 unsigned short get_checksum_mem(char *buff, int bytes)
1909 {
1910 return get_checksum(buff, bytes, 0);
1911 }
1912
1913
get_checksum_mem_buffer(struct file_buffer * file_buffer)1914 unsigned short get_checksum_mem_buffer(struct file_buffer *file_buffer)
1915 {
1916 if(file_buffer == NULL)
1917 return 0;
1918 else
1919 return get_checksum(file_buffer->data, file_buffer->size, 0);
1920 }
1921
1922
1923 #define DUP_HASH(a) (a & 0xffff)
add_file(long long start,long long file_size,long long file_bytes,unsigned int * block_listp,int blocks,unsigned int fragment,int offset,int bytes)1924 void add_file(long long start, long long file_size, long long file_bytes,
1925 unsigned int *block_listp, int blocks, unsigned int fragment,
1926 int offset, int bytes)
1927 {
1928 struct fragment *frg;
1929 unsigned int *block_list = block_listp;
1930 struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)];
1931 struct append_file *append_file;
1932 struct file_info *file;
1933
1934 if(!duplicate_checking || file_size == 0)
1935 return;
1936
1937 for(; dupl_ptr; dupl_ptr = dupl_ptr->next) {
1938 if(file_size != dupl_ptr->file_size)
1939 continue;
1940 if(blocks != 0 && start != dupl_ptr->start)
1941 continue;
1942 if(fragment != dupl_ptr->fragment->index)
1943 continue;
1944 if(fragment != SQUASHFS_INVALID_FRAG && (offset !=
1945 dupl_ptr->fragment->offset || bytes !=
1946 dupl_ptr->fragment->size))
1947 continue;
1948 return;
1949 }
1950
1951 frg = malloc(sizeof(struct fragment));
1952 if(frg == NULL)
1953 MEM_ERROR();
1954
1955 frg->index = fragment;
1956 frg->offset = offset;
1957 frg->size = bytes;
1958
1959 file = add_non_dup(file_size, file_bytes, block_list, start, frg, 0, 0,
1960 FALSE, FALSE);
1961
1962 if(fragment == SQUASHFS_INVALID_FRAG)
1963 return;
1964
1965 append_file = malloc(sizeof(struct append_file));
1966 if(append_file == NULL)
1967 MEM_ERROR();
1968
1969 append_file->file = file;
1970 append_file->next = file_mapping[fragment];
1971 file_mapping[fragment] = append_file;
1972 }
1973
1974
pre_duplicate(long long file_size)1975 int pre_duplicate(long long file_size)
1976 {
1977 struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)];
1978
1979 for(; dupl_ptr; dupl_ptr = dupl_ptr->next)
1980 if(dupl_ptr->file_size == file_size)
1981 return TRUE;
1982
1983 return FALSE;
1984 }
1985
1986
add_non_dup(long long file_size,long long bytes,unsigned int * block_list,long long start,struct fragment * fragment,unsigned short checksum,unsigned short fragment_checksum,int checksum_flag,int checksum_frag_flag)1987 struct file_info *add_non_dup(long long file_size, long long bytes,
1988 unsigned int *block_list, long long start, struct fragment *fragment,
1989 unsigned short checksum, unsigned short fragment_checksum,
1990 int checksum_flag, int checksum_frag_flag)
1991 {
1992 struct file_info *dupl_ptr = malloc(sizeof(struct file_info));
1993
1994 if(dupl_ptr == NULL)
1995 MEM_ERROR();
1996
1997 dupl_ptr->file_size = file_size;
1998 dupl_ptr->bytes = bytes;
1999 dupl_ptr->block_list = block_list;
2000 dupl_ptr->start = start;
2001 dupl_ptr->fragment = fragment;
2002 dupl_ptr->checksum = checksum;
2003 dupl_ptr->fragment_checksum = fragment_checksum;
2004 dupl_ptr->have_frag_checksum = checksum_frag_flag;
2005 dupl_ptr->have_checksum = checksum_flag;
2006
2007 pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
2008 pthread_mutex_lock(&dup_mutex);
2009 dupl_ptr->next = dupl[DUP_HASH(file_size)];
2010 dupl[DUP_HASH(file_size)] = dupl_ptr;
2011 dup_files ++;
2012 pthread_cleanup_pop(1);
2013
2014 return dupl_ptr;
2015 }
2016
2017
frag_duplicate(struct file_buffer * file_buffer,char * dont_put)2018 struct fragment *frag_duplicate(struct file_buffer *file_buffer, char *dont_put)
2019 {
2020 struct file_info *dupl_ptr;
2021 struct file_buffer *buffer;
2022 struct file_info *dupl_start = file_buffer->dupl_start;
2023 long long file_size = file_buffer->file_size;
2024 unsigned short checksum = file_buffer->checksum;
2025 int res;
2026
2027 if(file_buffer->duplicate) {
2028 TRACE("Found duplicate file, fragment %d, size %d, offset %d, "
2029 "checksum 0x%x\n", dupl_start->fragment->index,
2030 file_size, dupl_start->fragment->offset, checksum);
2031 *dont_put = TRUE;
2032 return dupl_start->fragment;
2033 } else {
2034 *dont_put = FALSE;
2035 dupl_ptr = dupl[DUP_HASH(file_size)];
2036 }
2037
2038 for(; dupl_ptr && dupl_ptr != dupl_start; dupl_ptr = dupl_ptr->next) {
2039 if(file_size == dupl_ptr->file_size && file_size ==
2040 dupl_ptr->fragment->size) {
2041 if(get_fragment_checksum(dupl_ptr) == checksum) {
2042 buffer = get_fragment(dupl_ptr->fragment);
2043 res = memcmp(file_buffer->data, buffer->data +
2044 dupl_ptr->fragment->offset, file_size);
2045 cache_block_put(buffer);
2046 if(res == 0)
2047 break;
2048 }
2049 }
2050 }
2051
2052 if(!dupl_ptr || dupl_ptr == dupl_start)
2053 return NULL;
2054
2055 TRACE("Found duplicate file, fragment %d, size %d, offset %d, "
2056 "checksum 0x%x\n", dupl_ptr->fragment->index, file_size,
2057 dupl_ptr->fragment->offset, checksum);
2058
2059 return dupl_ptr->fragment;
2060 }
2061
2062
duplicate(long long file_size,long long bytes,unsigned int ** block_list,long long * start,struct fragment ** fragment,struct file_buffer * file_buffer,int blocks,unsigned short checksum,int checksum_flag)2063 struct file_info *duplicate(long long file_size, long long bytes,
2064 unsigned int **block_list, long long *start, struct fragment **fragment,
2065 struct file_buffer *file_buffer, int blocks, unsigned short checksum,
2066 int checksum_flag)
2067 {
2068 struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)];
2069 int frag_bytes = file_buffer ? file_buffer->size : 0;
2070 unsigned short fragment_checksum = file_buffer ?
2071 file_buffer->checksum : 0;
2072
2073 for(; dupl_ptr; dupl_ptr = dupl_ptr->next)
2074 if(file_size == dupl_ptr->file_size && bytes == dupl_ptr->bytes
2075 && frag_bytes == dupl_ptr->fragment->size) {
2076 long long target_start, dup_start = dupl_ptr->start;
2077 int block;
2078
2079 if(memcmp(*block_list, dupl_ptr->block_list, blocks *
2080 sizeof(unsigned int)) != 0)
2081 continue;
2082
2083 if(checksum_flag == FALSE) {
2084 checksum = get_checksum_disk(*start, bytes,
2085 *block_list);
2086 checksum_flag = TRUE;
2087 }
2088
2089 if(!dupl_ptr->have_checksum) {
2090 dupl_ptr->checksum =
2091 get_checksum_disk(dupl_ptr->start,
2092 dupl_ptr->bytes, dupl_ptr->block_list);
2093 dupl_ptr->have_checksum = TRUE;
2094 }
2095
2096 if(checksum != dupl_ptr->checksum ||
2097 fragment_checksum !=
2098 get_fragment_checksum(dupl_ptr))
2099 continue;
2100
2101 target_start = *start;
2102 for(block = 0; block < blocks; block ++) {
2103 int size = SQUASHFS_COMPRESSED_SIZE_BLOCK
2104 ((*block_list)[block]);
2105 struct file_buffer *target_buffer = NULL;
2106 struct file_buffer *dup_buffer = NULL;
2107 char *target_data, *dup_data;
2108 int res;
2109
2110 if(size == 0)
2111 continue;
2112 target_buffer = cache_lookup(bwriter_buffer,
2113 target_start);
2114 if(target_buffer)
2115 target_data = target_buffer->data;
2116 else {
2117 target_data =
2118 read_from_disk(target_start,
2119 size);
2120 if(target_data == NULL) {
2121 ERROR("Failed to read data from"
2122 " output filesystem\n");
2123 BAD_ERROR("Output filesystem"
2124 " corrupted?\n");
2125 }
2126 }
2127
2128 dup_buffer = cache_lookup(bwriter_buffer,
2129 dup_start);
2130 if(dup_buffer)
2131 dup_data = dup_buffer->data;
2132 else {
2133 dup_data = read_from_disk2(dup_start,
2134 size);
2135 if(dup_data == NULL) {
2136 ERROR("Failed to read data from"
2137 " output filesystem\n");
2138 BAD_ERROR("Output filesystem"
2139 " corrupted?\n");
2140 }
2141 }
2142
2143 res = memcmp(target_data, dup_data, size);
2144 cache_block_put(target_buffer);
2145 cache_block_put(dup_buffer);
2146 if(res != 0)
2147 break;
2148 target_start += size;
2149 dup_start += size;
2150 }
2151 if(block == blocks) {
2152 struct file_buffer *frag_buffer =
2153 get_fragment(dupl_ptr->fragment);
2154
2155 if(frag_bytes == 0 ||
2156 memcmp(file_buffer->data,
2157 frag_buffer->data +
2158 dupl_ptr->fragment->offset,
2159 frag_bytes) == 0) {
2160 TRACE("Found duplicate file, start "
2161 "0x%llx, size %lld, checksum "
2162 "0x%x, fragment %d, size %d, "
2163 "offset %d, checksum 0x%x\n",
2164 dupl_ptr->start,
2165 dupl_ptr->bytes,
2166 dupl_ptr->checksum,
2167 dupl_ptr->fragment->index,
2168 frag_bytes,
2169 dupl_ptr->fragment->offset,
2170 fragment_checksum);
2171 *block_list = dupl_ptr->block_list;
2172 *start = dupl_ptr->start;
2173 *fragment = dupl_ptr->fragment;
2174 cache_block_put(frag_buffer);
2175 return 0;
2176 }
2177 cache_block_put(frag_buffer);
2178 }
2179 }
2180
2181
2182 return add_non_dup(file_size, bytes, *block_list, *start, *fragment,
2183 checksum, fragment_checksum, checksum_flag, TRUE);
2184 }
2185
2186
is_fragment(struct inode_info * inode)2187 static inline int is_fragment(struct inode_info *inode)
2188 {
2189 off_t file_size = inode->buf.st_size;
2190
2191 /*
2192 * If this block is to be compressed differently to the
2193 * fragment compression then it cannot be a fragment
2194 */
2195 if(inode->noF != noF)
2196 return FALSE;
2197
2198 return !inode->no_fragments && file_size && (file_size < block_size ||
2199 (inode->always_use_fragments && file_size & (block_size - 1)));
2200 }
2201
2202
put_file_buffer(struct file_buffer * file_buffer)2203 void put_file_buffer(struct file_buffer *file_buffer)
2204 {
2205 /*
2206 * Decide where to send the file buffer:
2207 * - compressible non-fragment blocks go to the deflate threads,
2208 * - fragments go to the process fragment threads,
2209 * - all others go directly to the main thread
2210 */
2211 if(file_buffer->error) {
2212 file_buffer->fragment = 0;
2213 seq_queue_put(to_main, file_buffer);
2214 } else if (file_buffer->file_size == 0)
2215 seq_queue_put(to_main, file_buffer);
2216 else if(file_buffer->fragment)
2217 queue_put(to_process_frag, file_buffer);
2218 else
2219 queue_put(to_deflate, file_buffer);
2220 }
2221
2222
2223 static int seq = 0;
reader_read_process(struct dir_ent * dir_ent)2224 void reader_read_process(struct dir_ent *dir_ent)
2225 {
2226 long long bytes = 0;
2227 struct inode_info *inode = dir_ent->inode;
2228 struct file_buffer *prev_buffer = NULL, *file_buffer;
2229 int status, byte, res, child;
2230 int file = pseudo_exec_file(get_pseudo_file(inode->pseudo_id), &child);
2231
2232 if(!file) {
2233 file_buffer = cache_get_nohash(reader_buffer);
2234 file_buffer->sequence = seq ++;
2235 goto read_err;
2236 }
2237
2238 while(1) {
2239 file_buffer = cache_get_nohash(reader_buffer);
2240 file_buffer->sequence = seq ++;
2241 file_buffer->noD = inode->noD;
2242
2243 byte = read_bytes(file, file_buffer->data, block_size);
2244 if(byte == -1)
2245 goto read_err2;
2246
2247 file_buffer->size = byte;
2248 file_buffer->file_size = -1;
2249 file_buffer->error = FALSE;
2250 file_buffer->fragment = FALSE;
2251 bytes += byte;
2252
2253 if(byte == 0)
2254 break;
2255
2256 /*
2257 * Update progress bar size. This is done
2258 * on every block rather than waiting for all blocks to be
2259 * read incase write_file_process() is running in parallel
2260 * with this. Otherwise the current progress bar position
2261 * may get ahead of the progress bar size.
2262 */
2263 progress_bar_size(1);
2264
2265 if(prev_buffer)
2266 put_file_buffer(prev_buffer);
2267 prev_buffer = file_buffer;
2268 }
2269
2270 /*
2271 * Update inode file size now that the size of the dynamic pseudo file
2272 * is known. This is needed for the -info option.
2273 */
2274 inode->buf.st_size = bytes;
2275
2276 res = waitpid(child, &status, 0);
2277 close(file);
2278
2279 if(res == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
2280 goto read_err;
2281
2282 if(prev_buffer == NULL)
2283 prev_buffer = file_buffer;
2284 else {
2285 cache_block_put(file_buffer);
2286 seq --;
2287 }
2288 prev_buffer->file_size = bytes;
2289 prev_buffer->fragment = is_fragment(inode);
2290 put_file_buffer(prev_buffer);
2291
2292 return;
2293
2294 read_err2:
2295 close(file);
2296 read_err:
2297 if(prev_buffer) {
2298 cache_block_put(file_buffer);
2299 seq --;
2300 file_buffer = prev_buffer;
2301 }
2302 file_buffer->error = TRUE;
2303 put_file_buffer(file_buffer);
2304 }
2305
2306
reader_read_file(struct dir_ent * dir_ent)2307 void reader_read_file(struct dir_ent *dir_ent)
2308 {
2309 struct stat *buf = &dir_ent->inode->buf, buf2;
2310 struct file_buffer *file_buffer;
2311 int blocks, file, res;
2312 long long bytes, read_size;
2313 struct inode_info *inode = dir_ent->inode;
2314
2315 if(inode->read)
2316 return;
2317
2318 inode->read = TRUE;
2319 again:
2320 bytes = 0;
2321 read_size = buf->st_size;
2322 blocks = (read_size + block_size - 1) >> block_log;
2323
2324 file = open(pathname_reader(dir_ent), O_RDONLY);
2325 if(file == -1) {
2326 file_buffer = cache_get_nohash(reader_buffer);
2327 file_buffer->sequence = seq ++;
2328 goto read_err2;
2329 }
2330
2331 do {
2332 file_buffer = cache_get_nohash(reader_buffer);
2333 file_buffer->file_size = read_size;
2334 file_buffer->sequence = seq ++;
2335 file_buffer->noD = inode->noD;
2336 file_buffer->error = FALSE;
2337
2338 /*
2339 * Always try to read block_size bytes from the file rather
2340 * than expected bytes (which will be less than the block_size
2341 * at the file tail) to check that the file hasn't grown
2342 * since being stated. If it is longer (or shorter) than
2343 * expected, then restat, and try again. Note the special
2344 * case where the file is an exact multiple of the block_size
2345 * is dealt with later.
2346 */
2347 file_buffer->size = read_bytes(file, file_buffer->data,
2348 block_size);
2349 if(file_buffer->size == -1)
2350 goto read_err;
2351
2352 bytes += file_buffer->size;
2353
2354 if(blocks > 1) {
2355 /* non-tail block should be exactly block_size */
2356 if(file_buffer->size < block_size)
2357 goto restat;
2358
2359 file_buffer->fragment = FALSE;
2360 put_file_buffer(file_buffer);
2361 }
2362 } while(-- blocks > 0);
2363
2364 /* Overall size including tail should match */
2365 if(read_size != bytes)
2366 goto restat;
2367
2368 if(read_size && read_size % block_size == 0) {
2369 /*
2370 * Special case where we've not tried to read past the end of
2371 * the file. We expect to get EOF, i.e. the file isn't larger
2372 * than we expect.
2373 */
2374 char buffer;
2375 int res;
2376
2377 res = read_bytes(file, &buffer, 1);
2378 if(res == -1)
2379 goto read_err;
2380
2381 if(res != 0)
2382 goto restat;
2383 }
2384
2385 file_buffer->fragment = is_fragment(inode);
2386 put_file_buffer(file_buffer);
2387
2388 close(file);
2389
2390 return;
2391
2392 restat:
2393 res = fstat(file, &buf2);
2394 if(res == -1) {
2395 ERROR("Cannot stat dir/file %s because %s\n",
2396 pathname_reader(dir_ent), strerror(errno));
2397 goto read_err;
2398 }
2399
2400 if(read_size != buf2.st_size) {
2401 close(file);
2402 memcpy(buf, &buf2, sizeof(struct stat));
2403 file_buffer->error = 2;
2404 put_file_buffer(file_buffer);
2405 goto again;
2406 }
2407 read_err:
2408 close(file);
2409 read_err2:
2410 file_buffer->error = TRUE;
2411 put_file_buffer(file_buffer);
2412 }
2413
2414
reader_scan(struct dir_info * dir)2415 void reader_scan(struct dir_info *dir) {
2416 struct dir_ent *dir_ent = dir->list;
2417
2418 for(; dir_ent; dir_ent = dir_ent->next) {
2419 struct stat *buf = &dir_ent->inode->buf;
2420 if(dir_ent->inode->root_entry)
2421 continue;
2422
2423 if(IS_PSEUDO_PROCESS(dir_ent->inode)) {
2424 reader_read_process(dir_ent);
2425 continue;
2426 }
2427
2428 switch(buf->st_mode & S_IFMT) {
2429 case S_IFREG:
2430 reader_read_file(dir_ent);
2431 break;
2432 case S_IFDIR:
2433 reader_scan(dir_ent->dir);
2434 break;
2435 }
2436 }
2437 }
2438
2439
reader(void * arg)2440 void *reader(void *arg)
2441 {
2442 if(!sorted)
2443 reader_scan(queue_get(to_reader));
2444 else {
2445 int i;
2446 struct priority_entry *entry;
2447
2448 queue_get(to_reader);
2449 for(i = 65535; i >= 0; i--)
2450 for(entry = priority_list[i]; entry;
2451 entry = entry->next)
2452 reader_read_file(entry->dir);
2453 }
2454
2455 pthread_exit(NULL);
2456 }
2457
2458
writer(void * arg)2459 void *writer(void *arg)
2460 {
2461 while(1) {
2462 struct file_buffer *file_buffer = queue_get(to_writer);
2463 off_t off;
2464
2465 if(file_buffer == NULL) {
2466 queue_put(from_writer, NULL);
2467 continue;
2468 }
2469
2470 off = file_buffer->block;
2471
2472 pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex);
2473 pthread_mutex_lock(&pos_mutex);
2474
2475 if(lseek(fd, off, SEEK_SET) == -1) {
2476 ERROR("writer: Lseek on destination failed because "
2477 "%s, offset=0x%llx\n", strerror(errno), off);
2478 BAD_ERROR("Probably out of space on output "
2479 "%s\n", block_device ? "block device" :
2480 "filesystem");
2481 }
2482
2483 if(write_bytes(fd, file_buffer->data,
2484 file_buffer->size) == -1)
2485 BAD_ERROR("Failed to write to output %s\n",
2486 block_device ? "block device" : "filesystem");
2487
2488 pthread_cleanup_pop(1);
2489
2490 cache_block_put(file_buffer);
2491 }
2492 }
2493
2494
all_zero(struct file_buffer * file_buffer)2495 int all_zero(struct file_buffer *file_buffer)
2496 {
2497 int i;
2498 long entries = file_buffer->size / sizeof(long);
2499 long *p = (long *) file_buffer->data;
2500
2501 for(i = 0; i < entries && p[i] == 0; i++);
2502
2503 if(i == entries) {
2504 for(i = file_buffer->size & ~(sizeof(long) - 1);
2505 i < file_buffer->size && file_buffer->data[i] == 0;
2506 i++);
2507
2508 return i == file_buffer->size;
2509 }
2510
2511 return 0;
2512 }
2513
2514
deflator(void * arg)2515 void *deflator(void *arg)
2516 {
2517 struct file_buffer *write_buffer = cache_get_nohash(bwriter_buffer);
2518 void *stream = NULL;
2519 int res;
2520
2521 res = compressor_init(comp, &stream, block_size, 1);
2522 if(res)
2523 BAD_ERROR("deflator:: compressor_init failed\n");
2524
2525 while(1) {
2526 struct file_buffer *file_buffer = queue_get(to_deflate);
2527
2528 if(sparse_files && all_zero(file_buffer)) {
2529 file_buffer->c_byte = 0;
2530 seq_queue_put(to_main, file_buffer);
2531 } else {
2532 write_buffer->c_byte = mangle2(stream,
2533 write_buffer->data, file_buffer->data,
2534 file_buffer->size, block_size,
2535 file_buffer->noD, 1);
2536 write_buffer->sequence = file_buffer->sequence;
2537 write_buffer->file_size = file_buffer->file_size;
2538 write_buffer->block = file_buffer->block;
2539 write_buffer->size = SQUASHFS_COMPRESSED_SIZE_BLOCK
2540 (write_buffer->c_byte);
2541 write_buffer->fragment = FALSE;
2542 write_buffer->error = FALSE;
2543 cache_block_put(file_buffer);
2544 seq_queue_put(to_main, write_buffer);
2545 write_buffer = cache_get_nohash(bwriter_buffer);
2546 }
2547 }
2548 }
2549
2550
frag_deflator(void * arg)2551 void *frag_deflator(void *arg)
2552 {
2553 void *stream = NULL;
2554 int res;
2555
2556 res = compressor_init(comp, &stream, block_size, 1);
2557 if(res)
2558 BAD_ERROR("frag_deflator:: compressor_init failed\n");
2559
2560 pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
2561
2562 while(1) {
2563 int c_byte, compressed_size;
2564 struct file_buffer *file_buffer = queue_get(to_frag);
2565 struct file_buffer *write_buffer =
2566 cache_get(fwriter_buffer, file_buffer->block);
2567
2568 c_byte = mangle2(stream, write_buffer->data, file_buffer->data,
2569 file_buffer->size, block_size, noF, 1);
2570 compressed_size = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
2571 write_buffer->size = compressed_size;
2572 pthread_mutex_lock(&fragment_mutex);
2573 if(fragments_locked == FALSE) {
2574 fragment_table[file_buffer->block].size = c_byte;
2575 fragment_table[file_buffer->block].start_block = bytes;
2576 write_buffer->block = bytes;
2577 bytes += compressed_size;
2578 fragments_outstanding --;
2579 queue_put(to_writer, write_buffer);
2580 pthread_mutex_unlock(&fragment_mutex);
2581 TRACE("Writing fragment %lld, uncompressed size %d, "
2582 "compressed size %d\n", file_buffer->block,
2583 file_buffer->size, compressed_size);
2584 } else {
2585 add_pending_fragment(write_buffer, c_byte,
2586 file_buffer->block);
2587 pthread_mutex_unlock(&fragment_mutex);
2588 }
2589 cache_block_put(file_buffer);
2590 }
2591
2592 pthread_cleanup_pop(0);
2593 }
2594
2595
get_file_buffer()2596 struct file_buffer *get_file_buffer()
2597 {
2598 struct file_buffer *file_buffer = seq_queue_get(to_main);
2599
2600 return file_buffer;
2601 }
2602
2603
write_file_empty(squashfs_inode * inode,struct dir_ent * dir_ent,struct file_buffer * file_buffer,int * duplicate_file)2604 void write_file_empty(squashfs_inode *inode, struct dir_ent *dir_ent,
2605 struct file_buffer *file_buffer, int *duplicate_file)
2606 {
2607 file_count ++;
2608 *duplicate_file = FALSE;
2609 cache_block_put(file_buffer);
2610 create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, 0, 0, 0,
2611 NULL, &empty_fragment, NULL, 0);
2612 }
2613
2614
write_file_frag(squashfs_inode * inode,struct dir_ent * dir_ent,struct file_buffer * file_buffer,int * duplicate_file)2615 void write_file_frag(squashfs_inode *inode, struct dir_ent *dir_ent,
2616 struct file_buffer *file_buffer, int *duplicate_file)
2617 {
2618 int size = file_buffer->file_size;
2619 struct fragment *fragment;
2620 unsigned short checksum = file_buffer->checksum;
2621 char dont_put;
2622
2623 fragment = frag_duplicate(file_buffer, &dont_put);
2624 *duplicate_file = !fragment;
2625 if(!fragment) {
2626 fragment = get_and_fill_fragment(file_buffer, dir_ent);
2627 if(duplicate_checking)
2628 add_non_dup(size, 0, NULL, 0, fragment, 0, checksum,
2629 TRUE, TRUE);
2630 }
2631
2632 if(dont_put)
2633 free(file_buffer);
2634 else
2635 cache_block_put(file_buffer);
2636
2637 total_bytes += size;
2638 file_count ++;
2639
2640 inc_progress_bar();
2641
2642 create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, size, 0,
2643 0, NULL, fragment, NULL, 0);
2644
2645 if(!duplicate_checking)
2646 free_fragment(fragment);
2647 }
2648
2649
write_file_process(squashfs_inode * inode,struct dir_ent * dir_ent,struct file_buffer * read_buffer,int * duplicate_file)2650 int write_file_process(squashfs_inode *inode, struct dir_ent *dir_ent,
2651 struct file_buffer *read_buffer, int *duplicate_file)
2652 {
2653 long long read_size, file_bytes, start;
2654 struct fragment *fragment;
2655 unsigned int *block_list = NULL;
2656 int block = 0, status;
2657 long long sparse = 0;
2658 struct file_buffer *fragment_buffer = NULL;
2659
2660 *duplicate_file = FALSE;
2661
2662 lock_fragments();
2663
2664 file_bytes = 0;
2665 start = bytes;
2666 while (1) {
2667 read_size = read_buffer->file_size;
2668 if(read_buffer->fragment)
2669 fragment_buffer = read_buffer;
2670 else {
2671 block_list = realloc(block_list, (block + 1) *
2672 sizeof(unsigned int));
2673 if(block_list == NULL)
2674 MEM_ERROR();
2675 block_list[block ++] = read_buffer->c_byte;
2676 if(read_buffer->c_byte) {
2677 read_buffer->block = bytes;
2678 bytes += read_buffer->size;
2679 cache_hash(read_buffer, read_buffer->block);
2680 file_bytes += read_buffer->size;
2681 queue_put(to_writer, read_buffer);
2682 } else {
2683 sparse += read_buffer->size;
2684 cache_block_put(read_buffer);
2685 }
2686 }
2687 inc_progress_bar();
2688
2689 if(read_size != -1)
2690 break;
2691
2692 read_buffer = get_file_buffer();
2693 if(read_buffer->error)
2694 goto read_err;
2695 }
2696
2697 unlock_fragments();
2698 fragment = get_and_fill_fragment(fragment_buffer, dir_ent);
2699
2700 if(duplicate_checking)
2701 add_non_dup(read_size, file_bytes, block_list, start, fragment,
2702 0, fragment_buffer ? fragment_buffer->checksum : 0,
2703 FALSE, TRUE);
2704 cache_block_put(fragment_buffer);
2705 file_count ++;
2706 total_bytes += read_size;
2707
2708 create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start,
2709 block, block_list, fragment, NULL, sparse);
2710
2711 if(duplicate_checking == FALSE) {
2712 free(block_list);
2713 free_fragment(fragment);
2714 }
2715
2716 return 0;
2717
2718 read_err:
2719 dec_progress_bar(block);
2720 status = read_buffer->error;
2721 bytes = start;
2722 if(!block_device) {
2723 int res;
2724
2725 queue_put(to_writer, NULL);
2726 if(queue_get(from_writer) != 0)
2727 EXIT_MKSQUASHFS();
2728 res = ftruncate(fd, bytes);
2729 if(res != 0)
2730 BAD_ERROR("Failed to truncate dest file because %s\n",
2731 strerror(errno));
2732 }
2733 unlock_fragments();
2734 free(block_list);
2735 cache_block_put(read_buffer);
2736 return status;
2737 }
2738
2739
write_file_blocks_dup(squashfs_inode * inode,struct dir_ent * dir_ent,struct file_buffer * read_buffer,int * duplicate_file)2740 int write_file_blocks_dup(squashfs_inode *inode, struct dir_ent *dir_ent,
2741 struct file_buffer *read_buffer, int *duplicate_file)
2742 {
2743 int block, thresh;
2744 long long read_size = read_buffer->file_size;
2745 long long file_bytes, dup_start, start;
2746 struct fragment *fragment;
2747 struct file_info *dupl_ptr;
2748 int blocks = (read_size + block_size - 1) >> block_log;
2749 unsigned int *block_list, *block_listp;
2750 struct file_buffer **buffer_list;
2751 int status;
2752 long long sparse = 0;
2753 struct file_buffer *fragment_buffer = NULL;
2754
2755 block_list = malloc(blocks * sizeof(unsigned int));
2756 if(block_list == NULL)
2757 MEM_ERROR();
2758 block_listp = block_list;
2759
2760 buffer_list = malloc(blocks * sizeof(struct file_buffer *));
2761 if(buffer_list == NULL)
2762 MEM_ERROR();
2763
2764 lock_fragments();
2765
2766 file_bytes = 0;
2767 start = dup_start = bytes;
2768 thresh = blocks > bwriter_size ? blocks - bwriter_size : 0;
2769
2770 for(block = 0; block < blocks;) {
2771 if(read_buffer->fragment) {
2772 block_list[block] = 0;
2773 buffer_list[block] = NULL;
2774 fragment_buffer = read_buffer;
2775 blocks = read_size >> block_log;
2776 } else {
2777 block_list[block] = read_buffer->c_byte;
2778
2779 if(read_buffer->c_byte) {
2780 read_buffer->block = bytes;
2781 bytes += read_buffer->size;
2782 file_bytes += read_buffer->size;
2783 cache_hash(read_buffer, read_buffer->block);
2784 if(block < thresh) {
2785 buffer_list[block] = NULL;
2786 queue_put(to_writer, read_buffer);
2787 } else
2788 buffer_list[block] = read_buffer;
2789 } else {
2790 buffer_list[block] = NULL;
2791 sparse += read_buffer->size;
2792 cache_block_put(read_buffer);
2793 }
2794 }
2795 inc_progress_bar();
2796
2797 if(++block < blocks) {
2798 read_buffer = get_file_buffer();
2799 if(read_buffer->error)
2800 goto read_err;
2801 }
2802 }
2803
2804 dupl_ptr = duplicate(read_size, file_bytes, &block_listp, &dup_start,
2805 &fragment, fragment_buffer, blocks, 0, FALSE);
2806
2807 if(dupl_ptr) {
2808 *duplicate_file = FALSE;
2809 for(block = thresh; block < blocks; block ++)
2810 if(buffer_list[block])
2811 queue_put(to_writer, buffer_list[block]);
2812 fragment = get_and_fill_fragment(fragment_buffer, dir_ent);
2813 dupl_ptr->fragment = fragment;
2814 } else {
2815 *duplicate_file = TRUE;
2816 for(block = thresh; block < blocks; block ++)
2817 cache_block_put(buffer_list[block]);
2818 bytes = start;
2819 if(thresh && !block_device) {
2820 int res;
2821
2822 queue_put(to_writer, NULL);
2823 if(queue_get(from_writer) != 0)
2824 EXIT_MKSQUASHFS();
2825 res = ftruncate(fd, bytes);
2826 if(res != 0)
2827 BAD_ERROR("Failed to truncate dest file because"
2828 " %s\n", strerror(errno));
2829 }
2830 }
2831
2832 unlock_fragments();
2833 cache_block_put(fragment_buffer);
2834 free(buffer_list);
2835 file_count ++;
2836 total_bytes += read_size;
2837
2838 /*
2839 * sparse count is needed to ensure squashfs correctly reports a
2840 * a smaller block count on stat calls to sparse files. This is
2841 * to ensure intelligent applications like cp correctly handle the
2842 * file as a sparse file. If the file in the original filesystem isn't
2843 * stored as a sparse file then still store it sparsely in squashfs, but
2844 * report it as non-sparse on stat calls to preserve semantics
2845 */
2846 if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size)
2847 sparse = 0;
2848
2849 create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size,
2850 dup_start, blocks, block_listp, fragment, NULL, sparse);
2851
2852 if(*duplicate_file == TRUE)
2853 free(block_list);
2854
2855 return 0;
2856
2857 read_err:
2858 dec_progress_bar(block);
2859 status = read_buffer->error;
2860 bytes = start;
2861 if(thresh && !block_device) {
2862 int res;
2863
2864 queue_put(to_writer, NULL);
2865 if(queue_get(from_writer) != 0)
2866 EXIT_MKSQUASHFS();
2867 res = ftruncate(fd, bytes);
2868 if(res != 0)
2869 BAD_ERROR("Failed to truncate dest file because %s\n",
2870 strerror(errno));
2871 }
2872 unlock_fragments();
2873 for(blocks = thresh; blocks < block; blocks ++)
2874 cache_block_put(buffer_list[blocks]);
2875 free(buffer_list);
2876 free(block_list);
2877 cache_block_put(read_buffer);
2878 return status;
2879 }
2880
2881
write_file_blocks(squashfs_inode * inode,struct dir_ent * dir_ent,struct file_buffer * read_buffer,int * dup)2882 int write_file_blocks(squashfs_inode *inode, struct dir_ent *dir_ent,
2883 struct file_buffer *read_buffer, int *dup)
2884 {
2885 long long read_size = read_buffer->file_size;
2886 long long file_bytes, start;
2887 struct fragment *fragment;
2888 unsigned int *block_list;
2889 int block, status;
2890 int blocks = (read_size + block_size - 1) >> block_log;
2891 long long sparse = 0;
2892 struct file_buffer *fragment_buffer = NULL;
2893
2894 if(pre_duplicate(read_size))
2895 return write_file_blocks_dup(inode, dir_ent, read_buffer, dup);
2896
2897 *dup = FALSE;
2898
2899 block_list = malloc(blocks * sizeof(unsigned int));
2900 if(block_list == NULL)
2901 MEM_ERROR();
2902
2903 lock_fragments();
2904
2905 file_bytes = 0;
2906 /* ANDROID CHANGES START*/
2907 #ifdef ANDROID
2908 if (align_4k_blocks && bytes % 4096) {
2909 bytes += 4096 - (bytes % 4096);
2910 }
2911 #endif
2912 /* ANDROID CHANGES END */
2913 start = bytes;
2914 for(block = 0; block < blocks;) {
2915 if(read_buffer->fragment) {
2916 block_list[block] = 0;
2917 fragment_buffer = read_buffer;
2918 blocks = read_size >> block_log;
2919 } else {
2920 block_list[block] = read_buffer->c_byte;
2921 if(read_buffer->c_byte) {
2922 read_buffer->block = bytes;
2923 bytes += read_buffer->size;
2924 cache_hash(read_buffer, read_buffer->block);
2925 file_bytes += read_buffer->size;
2926 queue_put(to_writer, read_buffer);
2927 } else {
2928 sparse += read_buffer->size;
2929 cache_block_put(read_buffer);
2930 }
2931 }
2932 inc_progress_bar();
2933
2934 if(++block < blocks) {
2935 read_buffer = get_file_buffer();
2936 if(read_buffer->error)
2937 goto read_err;
2938 }
2939 }
2940
2941 unlock_fragments();
2942 fragment = get_and_fill_fragment(fragment_buffer, dir_ent);
2943
2944 if(duplicate_checking)
2945 add_non_dup(read_size, file_bytes, block_list, start, fragment,
2946 0, fragment_buffer ? fragment_buffer->checksum : 0,
2947 FALSE, TRUE);
2948 cache_block_put(fragment_buffer);
2949 file_count ++;
2950 total_bytes += read_size;
2951
2952 /*
2953 * sparse count is needed to ensure squashfs correctly reports a
2954 * a smaller block count on stat calls to sparse files. This is
2955 * to ensure intelligent applications like cp correctly handle the
2956 * file as a sparse file. If the file in the original filesystem isn't
2957 * stored as a sparse file then still store it sparsely in squashfs, but
2958 * report it as non-sparse on stat calls to preserve semantics
2959 */
2960 if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size)
2961 sparse = 0;
2962
2963 create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start,
2964 blocks, block_list, fragment, NULL, sparse);
2965
2966 if(duplicate_checking == FALSE) {
2967 free(block_list);
2968 free_fragment(fragment);
2969 }
2970
2971 return 0;
2972
2973 read_err:
2974 dec_progress_bar(block);
2975 status = read_buffer->error;
2976 bytes = start;
2977 if(!block_device) {
2978 int res;
2979
2980 queue_put(to_writer, NULL);
2981 if(queue_get(from_writer) != 0)
2982 EXIT_MKSQUASHFS();
2983 res = ftruncate(fd, bytes);
2984 if(res != 0)
2985 BAD_ERROR("Failed to truncate dest file because %s\n",
2986 strerror(errno));
2987 }
2988 unlock_fragments();
2989 free(block_list);
2990 cache_block_put(read_buffer);
2991 return status;
2992 }
2993
2994
write_file(squashfs_inode * inode,struct dir_ent * dir,int * dup)2995 void write_file(squashfs_inode *inode, struct dir_ent *dir, int *dup)
2996 {
2997 int status;
2998 struct file_buffer *read_buffer;
2999
3000 again:
3001 read_buffer = get_file_buffer();
3002 status = read_buffer->error;
3003
3004 if(status)
3005 cache_block_put(read_buffer);
3006 else if(read_buffer->file_size == -1)
3007 status = write_file_process(inode, dir, read_buffer, dup);
3008 else if(read_buffer->file_size == 0)
3009 write_file_empty(inode, dir, read_buffer, dup);
3010 else if(read_buffer->fragment && read_buffer->c_byte)
3011 write_file_frag(inode, dir, read_buffer, dup);
3012 else
3013 status = write_file_blocks(inode, dir, read_buffer, dup);
3014
3015 if(status == 2) {
3016 ERROR("File %s changed size while reading filesystem, "
3017 "attempting to re-read\n", pathname(dir));
3018 goto again;
3019 } else if(status == 1) {
3020 ERROR_START("Failed to read file %s", pathname(dir));
3021 ERROR_EXIT(", creating empty file\n");
3022 write_file_empty(inode, dir, NULL, dup);
3023 }
3024 }
3025
3026
3027 #define BUFF_SIZE 512
3028 char *name;
3029 char *basename_r();
3030
getbase(char * pathname)3031 char *getbase(char *pathname)
3032 {
3033 static char *b_buffer = NULL;
3034 static int b_size = BUFF_SIZE;
3035 char *result;
3036
3037 if(b_buffer == NULL) {
3038 b_buffer = malloc(b_size);
3039 if(b_buffer == NULL)
3040 MEM_ERROR();
3041 }
3042
3043 while(1) {
3044 if(*pathname != '/') {
3045 result = getcwd(b_buffer, b_size);
3046 if(result == NULL && errno != ERANGE)
3047 BAD_ERROR("Getcwd failed in getbase\n");
3048
3049 /* enough room for pathname + "/" + '\0' terminator? */
3050 if(result && strlen(pathname) + 2 <=
3051 b_size - strlen(b_buffer)) {
3052 strcat(strcat(b_buffer, "/"), pathname);
3053 break;
3054 }
3055 } else if(strlen(pathname) < b_size) {
3056 strcpy(b_buffer, pathname);
3057 break;
3058 }
3059
3060 /* Buffer not large enough, realloc and try again */
3061 b_buffer = realloc(b_buffer, b_size += BUFF_SIZE);
3062 if(b_buffer == NULL)
3063 MEM_ERROR();
3064 }
3065
3066 name = b_buffer;
3067 if(((result = basename_r()) == NULL) || (strcmp(result, "..") == 0))
3068 return NULL;
3069 else
3070 return result;
3071 }
3072
3073
basename_r()3074 char *basename_r()
3075 {
3076 char *s;
3077 char *p;
3078 int n = 1;
3079
3080 for(;;) {
3081 s = name;
3082 if(*name == '\0')
3083 return NULL;
3084 if(*name != '/') {
3085 while(*name != '\0' && *name != '/') name++;
3086 n = name - s;
3087 }
3088 while(*name == '/') name++;
3089 if(strncmp(s, ".", n) == 0)
3090 continue;
3091 if((*name == '\0') || (strncmp(s, "..", n) == 0) ||
3092 ((p = basename_r()) == NULL)) {
3093 s[n] = '\0';
3094 return s;
3095 }
3096 if(strcmp(p, "..") == 0)
3097 continue;
3098 return p;
3099 }
3100 }
3101
3102
lookup_inode3(struct stat * buf,int pseudo,int id,char * symlink,int bytes)3103 struct inode_info *lookup_inode3(struct stat *buf, int pseudo, int id,
3104 char *symlink, int bytes)
3105 {
3106 int ino_hash = INODE_HASH(buf->st_dev, buf->st_ino);
3107 struct inode_info *inode;
3108
3109 /*
3110 * Look-up inode in hash table, if it already exists we have a
3111 * hard-link, so increment the nlink count and return it.
3112 * Don't do the look-up for directories because we don't hard-link
3113 * directories.
3114 */
3115 if ((buf->st_mode & S_IFMT) != S_IFDIR) {
3116 for(inode = inode_info[ino_hash]; inode; inode = inode->next) {
3117 if(memcmp(buf, &inode->buf, sizeof(struct stat)) == 0) {
3118 inode->nlink ++;
3119 return inode;
3120 }
3121 }
3122 }
3123
3124 inode = malloc(sizeof(struct inode_info) + bytes);
3125 if(inode == NULL)
3126 MEM_ERROR();
3127
3128 if(bytes)
3129 memcpy(&inode->symlink, symlink, bytes);
3130 memcpy(&inode->buf, buf, sizeof(struct stat));
3131 inode->read = FALSE;
3132 inode->root_entry = FALSE;
3133 inode->pseudo_file = pseudo;
3134 inode->pseudo_id = id;
3135 inode->inode = SQUASHFS_INVALID_BLK;
3136 inode->nlink = 1;
3137 inode->inode_number = 0;
3138
3139 /*
3140 * Copy filesystem wide defaults into inode, these filesystem
3141 * wide defaults may be altered on an individual inode basis by
3142 * user specified actions
3143 *
3144 */
3145 inode->no_fragments = no_fragments;
3146 inode->always_use_fragments = always_use_fragments;
3147
3148 /* ANDROID CHANGES START*/
3149 #ifdef ANDROID
3150 /* Check the whitelist */
3151 inode->noD = whitelisted(buf);
3152 #else
3153 inode->noD = noD;
3154 #endif
3155 /* ANDROID CHANGES END */
3156
3157 inode->noF = noF;
3158
3159 inode->next = inode_info[ino_hash];
3160 inode_info[ino_hash] = inode;
3161
3162 return inode;
3163 }
3164
3165
lookup_inode2(struct stat * buf,int pseudo,int id)3166 static inline struct inode_info *lookup_inode2(struct stat *buf, int pseudo, int id)
3167 {
3168 return lookup_inode3(buf, pseudo, id, NULL, 0);
3169 }
3170
3171
lookup_inode(struct stat * buf)3172 static inline struct inode_info *lookup_inode(struct stat *buf)
3173 {
3174 return lookup_inode2(buf, 0, 0);
3175 }
3176
3177
alloc_inode_no(struct inode_info * inode,unsigned int use_this)3178 static inline void alloc_inode_no(struct inode_info *inode, unsigned int use_this)
3179 {
3180 if (inode->inode_number == 0) {
3181 inode->inode_number = use_this ? : inode_no ++;
3182 if((inode->buf.st_mode & S_IFMT) == S_IFREG)
3183 progress_bar_size((inode->buf.st_size + block_size - 1)
3184 >> block_log);
3185 }
3186 }
3187
3188
create_dir_entry(char * name,char * source_name,char * nonstandard_pathname,struct dir_info * dir)3189 static inline struct dir_ent *create_dir_entry(char *name, char *source_name,
3190 char *nonstandard_pathname, struct dir_info *dir)
3191 {
3192 struct dir_ent *dir_ent = malloc(sizeof(struct dir_ent));
3193 if(dir_ent == NULL)
3194 MEM_ERROR();
3195
3196 dir_ent->name = name;
3197 dir_ent->source_name = source_name;
3198 dir_ent->nonstandard_pathname = nonstandard_pathname;
3199 dir_ent->our_dir = dir;
3200 dir_ent->inode = NULL;
3201 dir_ent->next = NULL;
3202 /* ANDROID CHANGES START*/
3203 #ifdef ANDROID
3204 dir_ent->capabilities = 0;
3205 #endif
3206 /* ANDROID CHANGES END */
3207
3208 return dir_ent;
3209 }
3210
3211
add_dir_entry(struct dir_ent * dir_ent,struct dir_info * sub_dir,struct inode_info * inode_info)3212 static inline void add_dir_entry(struct dir_ent *dir_ent, struct dir_info *sub_dir,
3213 struct inode_info *inode_info)
3214 {
3215 struct dir_info *dir = dir_ent->our_dir;
3216
3217 if(sub_dir)
3218 sub_dir->dir_ent = dir_ent;
3219
3220 /* ANDROID CHANGES START*/
3221 #ifdef ANDROID
3222 if (android_config) {
3223 if (mount_point) {
3224 char *mounted_path;
3225 char *rel_path;
3226
3227 alloc_mounted_path(mount_point, subpathname(dir_ent), &mounted_path);
3228 rel_path = mounted_path;
3229 while (rel_path && *rel_path == '/')
3230 rel_path++;
3231 android_fs_config(fs_config_func, rel_path, &inode_info->buf, target_out_path, &dir_ent->capabilities);
3232 free(mounted_path);
3233 } else {
3234 android_fs_config(fs_config_func, pathname(dir_ent), &inode_info->buf, target_out_path, &dir_ent->capabilities);
3235 }
3236 }
3237 #endif
3238 /* ANDROID CHANGES END */
3239
3240 dir_ent->inode = inode_info;
3241 dir_ent->dir = sub_dir;
3242
3243 dir_ent->next = dir->list;
3244 dir->list = dir_ent;
3245 dir->count++;
3246 }
3247
add_dir_entry2(char * name,char * source_name,char * nonstandard_pathname,struct dir_info * sub_dir,struct inode_info * inode_info,struct dir_info * dir)3248 static inline void add_dir_entry2(char *name, char *source_name,
3249 char *nonstandard_pathname, struct dir_info *sub_dir,
3250 struct inode_info *inode_info, struct dir_info *dir)
3251 {
3252 struct dir_ent *dir_ent = create_dir_entry(name, source_name,
3253 nonstandard_pathname, dir);
3254
3255
3256 add_dir_entry(dir_ent, sub_dir, inode_info);
3257 }
3258
3259
free_dir_entry(struct dir_ent * dir_ent)3260 static inline void free_dir_entry(struct dir_ent *dir_ent)
3261 {
3262 if(dir_ent->name)
3263 free(dir_ent->name);
3264
3265 if(dir_ent->source_name)
3266 free(dir_ent->source_name);
3267
3268 if(dir_ent->nonstandard_pathname)
3269 free(dir_ent->nonstandard_pathname);
3270
3271 /* if this entry has been associated with an inode, then we need
3272 * to update the inode nlink count. Orphaned inodes are harmless, and
3273 * is easier to leave them than go to the bother of deleting them */
3274 if(dir_ent->inode && !dir_ent->inode->root_entry)
3275 dir_ent->inode->nlink --;
3276
3277 free(dir_ent);
3278 }
3279
3280
add_excluded(struct dir_info * dir)3281 static inline void add_excluded(struct dir_info *dir)
3282 {
3283 dir->excluded ++;
3284 }
3285
3286
dir_scan(squashfs_inode * inode,char * pathname,struct dir_ent * (_readdir)(struct dir_info *),int progress)3287 void dir_scan(squashfs_inode *inode, char *pathname,
3288 struct dir_ent *(_readdir)(struct dir_info *), int progress)
3289 {
3290 struct stat buf;
3291 struct dir_ent *dir_ent;
3292 /* ANDROID CHANGES START*/
3293 #ifdef ANDROID
3294 uint64_t caps = 0;
3295 #endif
3296 /* ANDROID CHANGES END */
3297
3298 root_dir = dir_scan1(pathname, "", paths, _readdir, 1);
3299 if(root_dir == NULL)
3300 return;
3301
3302 /* Create root directory dir_ent and associated inode, and connect
3303 * it to the root directory dir_info structure */
3304 dir_ent = create_dir_entry("", NULL, pathname,
3305 scan1_opendir("", "", 0));
3306
3307 if(pathname[0] == '\0') {
3308 /*
3309 * dummy top level directory, if multiple sources specified on
3310 * command line
3311 */
3312 memset(&buf, 0, sizeof(buf));
3313 buf.st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR;
3314 buf.st_uid = getuid();
3315 buf.st_gid = getgid();
3316 buf.st_mtime = time(NULL);
3317 buf.st_dev = 0;
3318 buf.st_ino = 0;
3319 dir_ent->inode = lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0);
3320 } else {
3321 if(lstat(pathname, &buf) == -1)
3322 /* source directory has disappeared? */
3323 BAD_ERROR("Cannot stat source directory %s because %s\n",
3324 pathname, strerror(errno));
3325 /* ANDROID CHANGES START*/
3326 #ifdef ANDROID
3327 buf.st_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_IFDIR; // root mode
3328 buf.st_uid = 0;
3329 buf.st_gid = 0;
3330 buf.st_mtime = time(NULL);
3331 buf.st_dev = 0;
3332 buf.st_ino = 0;
3333 #endif
3334 /* ANDROID CHANGES END */
3335 dir_ent->inode = lookup_inode(&buf);
3336 }
3337
3338 /* ANDROID CHANGES START*/
3339 #ifdef ANDROID
3340 dir_ent->capabilities = caps;
3341 if (android_config) {
3342 android_fs_config(fs_config_func, "", &dir_ent->inode->buf, target_out_path, &dir_ent->capabilities);
3343 }
3344 #endif
3345 /* ANDROID CHANGES END */
3346
3347 dir_ent->dir = root_dir;
3348 root_dir->dir_ent = dir_ent;
3349
3350 /*
3351 * Process most actions and any pseudo files
3352 */
3353 if(actions() || get_pseudo())
3354 dir_scan2(root_dir, get_pseudo());
3355
3356 /*
3357 * Process move actions
3358 */
3359 if(move_actions()) {
3360 dir_scan3(root_dir);
3361 do_move_actions();
3362 }
3363
3364 /*
3365 * Process prune actions
3366 */
3367 if(prune_actions())
3368 dir_scan4(root_dir);
3369
3370 /*
3371 * Process empty actions
3372 */
3373 if(empty_actions())
3374 dir_scan5(root_dir);
3375
3376 /*
3377 * Sort directories and compute the inode numbers
3378 */
3379 dir_scan6(root_dir);
3380
3381 alloc_inode_no(dir_ent->inode, root_inode_number);
3382
3383 eval_actions(root_dir, dir_ent);
3384
3385 if(sorted)
3386 generate_file_priorities(root_dir, 0,
3387 &root_dir->dir_ent->inode->buf);
3388
3389 if(appending) {
3390 sigset_t sigmask;
3391
3392 restore_thread = init_restore_thread();
3393 sigemptyset(&sigmask);
3394 sigaddset(&sigmask, SIGINT);
3395 sigaddset(&sigmask, SIGTERM);
3396 sigaddset(&sigmask, SIGUSR1);
3397 if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1)
3398 BAD_ERROR("Failed to set signal mask\n");
3399 write_destination(fd, SQUASHFS_START, 4, "\0\0\0\0");
3400 }
3401
3402 queue_put(to_reader, root_dir);
3403
3404 set_progressbar_state(progress);
3405
3406 if(sorted)
3407 sort_files_and_write(root_dir);
3408
3409 dir_scan7(inode, root_dir);
3410 dir_ent->inode->inode = *inode;
3411 dir_ent->inode->type = SQUASHFS_DIR_TYPE;
3412 }
3413
3414
3415 /*
3416 * dir_scan1 routines...
3417 * These scan the source directories into memory for processing.
3418 * Exclude actions are processed here (in contrast to the other actions)
3419 * because they affect what is scanned.
3420 */
scan1_opendir(char * pathname,char * subpath,int depth)3421 struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth)
3422 {
3423 struct dir_info *dir;
3424
3425 dir = malloc(sizeof(struct dir_info));
3426 if(dir == NULL)
3427 MEM_ERROR();
3428
3429 if(pathname[0] != '\0') {
3430 dir->linuxdir = opendir(pathname);
3431 if(dir->linuxdir == NULL) {
3432 free(dir);
3433 return NULL;
3434 }
3435 }
3436
3437 dir->pathname = strdup(pathname);
3438 dir->subpath = strdup(subpath);
3439 dir->count = 0;
3440 dir->directory_count = 0;
3441 dir->dir_is_ldir = TRUE;
3442 dir->list = NULL;
3443 dir->depth = depth;
3444 dir->excluded = 0;
3445
3446 return dir;
3447 }
3448
3449
scan1_encomp_readdir(struct dir_info * dir)3450 struct dir_ent *scan1_encomp_readdir(struct dir_info *dir)
3451 {
3452 static int index = 0;
3453
3454 if(dir->count < old_root_entries) {
3455 int i;
3456
3457 for(i = 0; i < old_root_entries; i++) {
3458 if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE)
3459 dir->directory_count ++;
3460 add_dir_entry2(old_root_entry[i].name, NULL, NULL, NULL,
3461 &old_root_entry[i].inode, dir);
3462 }
3463 }
3464
3465 while(index < source) {
3466 char *basename = NULL;
3467 char *dir_name = getbase(source_path[index]);
3468 int pass = 1, res;
3469
3470 if(dir_name == NULL) {
3471 ERROR_START("Bad source directory %s",
3472 source_path[index]);
3473 ERROR_EXIT(" - skipping ...\n");
3474 index ++;
3475 continue;
3476 }
3477 dir_name = strdup(dir_name);
3478 for(;;) {
3479 struct dir_ent *dir_ent = dir->list;
3480
3481 for(; dir_ent && strcmp(dir_ent->name, dir_name) != 0;
3482 dir_ent = dir_ent->next);
3483 if(dir_ent == NULL)
3484 break;
3485 ERROR("Source directory entry %s already used! - trying"
3486 " ", dir_name);
3487 if(pass == 1)
3488 basename = dir_name;
3489 else
3490 free(dir_name);
3491 res = asprintf(&dir_name, "%s_%d", basename, pass++);
3492 if(res == -1)
3493 BAD_ERROR("asprintf failed in "
3494 "scan1_encomp_readdir\n");
3495 ERROR("%s\n", dir_name);
3496 }
3497 return create_dir_entry(dir_name, basename,
3498 strdup(source_path[index ++]), dir);
3499 }
3500 return NULL;
3501 }
3502
3503
scan1_single_readdir(struct dir_info * dir)3504 struct dir_ent *scan1_single_readdir(struct dir_info *dir)
3505 {
3506 struct dirent *d_name;
3507 int i;
3508
3509 if(dir->count < old_root_entries) {
3510 for(i = 0; i < old_root_entries; i++) {
3511 if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE)
3512 dir->directory_count ++;
3513 add_dir_entry2(old_root_entry[i].name, NULL, NULL, NULL,
3514 &old_root_entry[i].inode, dir);
3515 }
3516 }
3517
3518 if((d_name = readdir(dir->linuxdir)) != NULL) {
3519 char *basename = NULL;
3520 char *dir_name = strdup(d_name->d_name);
3521 int pass = 1, res;
3522
3523 for(;;) {
3524 struct dir_ent *dir_ent = dir->list;
3525
3526 for(; dir_ent && strcmp(dir_ent->name, dir_name) != 0;
3527 dir_ent = dir_ent->next);
3528 if(dir_ent == NULL)
3529 break;
3530 ERROR("Source directory entry %s already used! - trying"
3531 " ", dir_name);
3532 if (pass == 1)
3533 basename = dir_name;
3534 else
3535 free(dir_name);
3536 res = asprintf(&dir_name, "%s_%d", d_name->d_name, pass++);
3537 if(res == -1)
3538 BAD_ERROR("asprintf failed in "
3539 "scan1_single_readdir\n");
3540 ERROR("%s\n", dir_name);
3541 }
3542 return create_dir_entry(dir_name, basename, NULL, dir);
3543 }
3544
3545 return NULL;
3546 }
3547
3548
scan1_readdir(struct dir_info * dir)3549 struct dir_ent *scan1_readdir(struct dir_info *dir)
3550 {
3551 struct dirent *d_name = readdir(dir->linuxdir);
3552
3553 return d_name ?
3554 create_dir_entry(strdup(d_name->d_name), NULL, NULL, dir) :
3555 NULL;
3556 }
3557
3558
scan1_freedir(struct dir_info * dir)3559 void scan1_freedir(struct dir_info *dir)
3560 {
3561 if(dir->pathname[0] != '\0')
3562 closedir(dir->linuxdir);
3563 }
3564
3565
dir_scan1(char * filename,char * subpath,struct pathnames * paths,struct dir_ent * (_readdir)(struct dir_info *),int depth)3566 struct dir_info *dir_scan1(char *filename, char *subpath,
3567 struct pathnames *paths,
3568 struct dir_ent *(_readdir)(struct dir_info *), int depth)
3569 {
3570 struct dir_info *dir = scan1_opendir(filename, subpath, depth);
3571 struct dir_ent *dir_ent;
3572
3573 if(dir == NULL) {
3574 ERROR_START("Could not open %s", filename);
3575 ERROR_EXIT(", skipping...\n");
3576 return NULL;
3577 }
3578
3579 while((dir_ent = _readdir(dir))) {
3580 struct dir_info *sub_dir;
3581 struct stat buf;
3582 struct pathnames *new = NULL;
3583 char *filename = pathname(dir_ent);
3584 char *subpath = NULL;
3585 char *dir_name = dir_ent->name;
3586
3587 if(strcmp(dir_name, ".") == 0 || strcmp(dir_name, "..") == 0) {
3588 free_dir_entry(dir_ent);
3589 continue;
3590 }
3591
3592 if(lstat(filename, &buf) == -1) {
3593 ERROR_START("Cannot stat dir/file %s because %s",
3594 filename, strerror(errno));
3595 ERROR_EXIT(", ignoring\n");
3596 free_dir_entry(dir_ent);
3597 continue;
3598 }
3599
3600 if((buf.st_mode & S_IFMT) != S_IFREG &&
3601 (buf.st_mode & S_IFMT) != S_IFDIR &&
3602 (buf.st_mode & S_IFMT) != S_IFLNK &&
3603 (buf.st_mode & S_IFMT) != S_IFCHR &&
3604 (buf.st_mode & S_IFMT) != S_IFBLK &&
3605 (buf.st_mode & S_IFMT) != S_IFIFO &&
3606 (buf.st_mode & S_IFMT) != S_IFSOCK) {
3607 ERROR_START("File %s has unrecognised filetype %d",
3608 filename, buf.st_mode & S_IFMT);
3609 ERROR_EXIT(", ignoring\n");
3610 free_dir_entry(dir_ent);
3611 continue;
3612 }
3613
3614 if((old_exclude && old_excluded(filename, &buf)) ||
3615 (!old_exclude && excluded(dir_name, paths, &new))) {
3616 add_excluded(dir);
3617 free_dir_entry(dir_ent);
3618 continue;
3619 }
3620
3621 if(exclude_actions()) {
3622 subpath = subpathname(dir_ent);
3623
3624 if(eval_exclude_actions(dir_name, filename, subpath,
3625 &buf, depth, dir_ent)) {
3626 add_excluded(dir);
3627 free_dir_entry(dir_ent);
3628 continue;
3629 }
3630 }
3631
3632 switch(buf.st_mode & S_IFMT) {
3633 case S_IFDIR:
3634 if(subpath == NULL)
3635 subpath = subpathname(dir_ent);
3636
3637 sub_dir = dir_scan1(filename, subpath, new,
3638 scan1_readdir, depth + 1);
3639 if(sub_dir) {
3640 dir->directory_count ++;
3641 add_dir_entry(dir_ent, sub_dir,
3642 lookup_inode(&buf));
3643 } else
3644 free_dir_entry(dir_ent);
3645 break;
3646 case S_IFLNK: {
3647 int byte;
3648 static char buff[65536]; /* overflow safe */
3649
3650 byte = readlink(filename, buff, 65536);
3651 if(byte == -1) {
3652 ERROR_START("Failed to read symlink %s",
3653 filename);
3654 ERROR_EXIT(", ignoring\n");
3655 } else if(byte == 65536) {
3656 ERROR_START("Symlink %s is greater than 65536 "
3657 "bytes!", filename);
3658 ERROR_EXIT(", ignoring\n");
3659 } else {
3660 /* readlink doesn't 0 terminate the returned
3661 * path */
3662 buff[byte] = '\0';
3663 add_dir_entry(dir_ent, NULL, lookup_inode3(&buf,
3664 0, 0, buff, byte + 1));
3665 }
3666 break;
3667 }
3668 default:
3669 add_dir_entry(dir_ent, NULL, lookup_inode(&buf));
3670 }
3671
3672 free(new);
3673 }
3674
3675 scan1_freedir(dir);
3676
3677 return dir;
3678 }
3679
3680
3681 /*
3682 * dir_scan2 routines...
3683 * This processes most actions and any pseudo files
3684 */
scan2_readdir(struct dir_info * dir,struct dir_ent * dir_ent)3685 struct dir_ent *scan2_readdir(struct dir_info *dir, struct dir_ent *dir_ent)
3686 {
3687 if (dir_ent == NULL)
3688 dir_ent = dir->list;
3689 else
3690 dir_ent = dir_ent->next;
3691
3692 for(; dir_ent && dir_ent->inode->root_entry; dir_ent = dir_ent->next);
3693
3694 return dir_ent;
3695 }
3696
3697
scan2_lookup(struct dir_info * dir,char * name)3698 struct dir_ent *scan2_lookup(struct dir_info *dir, char *name)
3699 {
3700 struct dir_ent *dir_ent = dir->list;
3701
3702 for(; dir_ent && strcmp(dir_ent->name, name) != 0;
3703 dir_ent = dir_ent->next);
3704
3705 return dir_ent;
3706 }
3707
3708
dir_scan2(struct dir_info * dir,struct pseudo * pseudo)3709 void dir_scan2(struct dir_info *dir, struct pseudo *pseudo)
3710 {
3711 struct dir_ent *dir_ent = NULL;
3712 struct pseudo_entry *pseudo_ent;
3713 struct stat buf;
3714 static int pseudo_ino = 1;
3715
3716 while((dir_ent = scan2_readdir(dir, dir_ent)) != NULL) {
3717 struct inode_info *inode_info = dir_ent->inode;
3718 struct stat *buf = &inode_info->buf;
3719 char *name = dir_ent->name;
3720
3721 eval_actions(root_dir, dir_ent);
3722
3723 if((buf->st_mode & S_IFMT) == S_IFDIR)
3724 dir_scan2(dir_ent->dir, pseudo_subdir(name, pseudo));
3725 }
3726
3727 while((pseudo_ent = pseudo_readdir(pseudo)) != NULL) {
3728 dir_ent = scan2_lookup(dir, pseudo_ent->name);
3729 if(pseudo_ent->dev->type == 'm') {
3730 struct stat *buf;
3731 if(dir_ent == NULL) {
3732 ERROR_START("Pseudo modify file \"%s\" does "
3733 "not exist in source filesystem.",
3734 pseudo_ent->pathname);
3735 ERROR_EXIT(" Ignoring.\n");
3736 continue;
3737 }
3738 if(dir_ent->inode->root_entry) {
3739 ERROR_START("Pseudo modify file \"%s\" is a "
3740 "pre-existing file in the filesystem "
3741 "being appended to. It cannot be "\
3742 "modified.", pseudo_ent->pathname);
3743 ERROR_EXIT(" Ignoring.\n");
3744 continue;
3745 }
3746 buf = &dir_ent->inode->buf;
3747 buf->st_mode = (buf->st_mode & S_IFMT) |
3748 pseudo_ent->dev->mode;
3749 buf->st_uid = pseudo_ent->dev->uid;
3750 buf->st_gid = pseudo_ent->dev->gid;
3751 continue;
3752 }
3753
3754 if(dir_ent) {
3755 if(dir_ent->inode->root_entry) {
3756 ERROR_START("Pseudo file \"%s\" is a "
3757 "pre-existing file in the filesystem "
3758 "being appended to.",
3759 pseudo_ent->pathname);
3760 ERROR_EXIT(" Ignoring.\n");
3761 } else {
3762 ERROR_START("Pseudo file \"%s\" exists in "
3763 "source filesystem \"%s\".",
3764 pseudo_ent->pathname,
3765 pathname(dir_ent));
3766 ERROR_EXIT("\nIgnoring, exclude it (-e/-ef) to "
3767 "override.\n");
3768 }
3769 continue;
3770 }
3771
3772 memset(&buf, 0, sizeof(buf));
3773 buf.st_mode = pseudo_ent->dev->mode;
3774 buf.st_uid = pseudo_ent->dev->uid;
3775 buf.st_gid = pseudo_ent->dev->gid;
3776 buf.st_rdev = makedev(pseudo_ent->dev->major,
3777 pseudo_ent->dev->minor);
3778 buf.st_mtime = time(NULL);
3779 buf.st_ino = pseudo_ino ++;
3780
3781 if(pseudo_ent->dev->type == 'd') {
3782 struct dir_ent *dir_ent =
3783 create_dir_entry(pseudo_ent->name, NULL,
3784 pseudo_ent->pathname, dir);
3785 char *subpath = strdup(subpathname(dir_ent));
3786 struct dir_info *sub_dir = scan1_opendir("", subpath,
3787 dir->depth + 1);
3788 if(sub_dir == NULL) {
3789 ERROR_START("Could not create pseudo directory "
3790 "\"%s\"", pseudo_ent->pathname);
3791 ERROR_EXIT(", skipping...\n");
3792 free(subpath);
3793 pseudo_ino --;
3794 continue;
3795 }
3796 dir_scan2(sub_dir, pseudo_ent->pseudo);
3797 dir->directory_count ++;
3798 add_dir_entry(dir_ent, sub_dir,
3799 lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0));
3800 } else if(pseudo_ent->dev->type == 'f') {
3801 add_dir_entry2(pseudo_ent->name, NULL,
3802 pseudo_ent->pathname, NULL,
3803 lookup_inode2(&buf, PSEUDO_FILE_PROCESS,
3804 pseudo_ent->dev->pseudo_id), dir);
3805 } else {
3806 add_dir_entry2(pseudo_ent->name, NULL,
3807 pseudo_ent->pathname, NULL,
3808 lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0), dir);
3809 }
3810 }
3811 }
3812
3813
3814 /*
3815 * dir_scan3 routines...
3816 * This processes the move action
3817 */
dir_scan3(struct dir_info * dir)3818 void dir_scan3(struct dir_info *dir)
3819 {
3820 struct dir_ent *dir_ent = NULL;
3821
3822 while((dir_ent = scan2_readdir(dir, dir_ent)) != NULL) {
3823
3824 eval_move_actions(root_dir, dir_ent);
3825
3826 if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
3827 dir_scan3(dir_ent->dir);
3828 }
3829 }
3830
3831
3832 /*
3833 * dir_scan4 routines...
3834 * This processes the prune action. This action is designed to do fine
3835 * grained tuning of the in-core directory structure after the exclude,
3836 * move and pseudo actions have been performed. This allows complex
3837 * tests to be performed which are impossible at exclude time (i.e.
3838 * tests which rely on the in-core directory structure)
3839 */
free_dir(struct dir_info * dir)3840 void free_dir(struct dir_info *dir)
3841 {
3842 struct dir_ent *dir_ent = dir->list;
3843
3844 while(dir_ent) {
3845 struct dir_ent *tmp = dir_ent;
3846
3847 if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
3848 free_dir(dir_ent->dir);
3849
3850 dir_ent = dir_ent->next;
3851 free_dir_entry(tmp);
3852 }
3853
3854 free(dir->pathname);
3855 free(dir->subpath);
3856 free(dir);
3857 }
3858
3859
dir_scan4(struct dir_info * dir)3860 void dir_scan4(struct dir_info *dir)
3861 {
3862 struct dir_ent *dir_ent = dir->list, *prev = NULL;
3863
3864 while(dir_ent) {
3865 if(dir_ent->inode->root_entry) {
3866 prev = dir_ent;
3867 dir_ent = dir_ent->next;
3868 continue;
3869 }
3870
3871 if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
3872 dir_scan4(dir_ent->dir);
3873
3874 if(eval_prune_actions(root_dir, dir_ent)) {
3875 struct dir_ent *tmp = dir_ent;
3876
3877 if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) {
3878 free_dir(dir_ent->dir);
3879 dir->directory_count --;
3880 }
3881
3882 dir->count --;
3883
3884 /* remove dir_ent from list */
3885 dir_ent = dir_ent->next;
3886 if(prev)
3887 prev->next = dir_ent;
3888 else
3889 dir->list = dir_ent;
3890
3891 /* free it */
3892 free_dir_entry(tmp);
3893
3894 add_excluded(dir);
3895 continue;
3896 }
3897
3898 prev = dir_ent;
3899 dir_ent = dir_ent->next;
3900 }
3901 }
3902
3903
3904 /*
3905 * dir_scan5 routines...
3906 * This processes the empty action. This action has to be processed after
3907 * all other actions because the previous exclude and move actions and the
3908 * pseudo actions affect whether a directory is empty
3909 */
dir_scan5(struct dir_info * dir)3910 void dir_scan5(struct dir_info *dir)
3911 {
3912 struct dir_ent *dir_ent = dir->list, *prev = NULL;
3913
3914 while(dir_ent) {
3915 if(dir_ent->inode->root_entry) {
3916 prev = dir_ent;
3917 dir_ent = dir_ent->next;
3918 continue;
3919 }
3920
3921 if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) {
3922 dir_scan5(dir_ent->dir);
3923
3924 if(eval_empty_actions(root_dir, dir_ent)) {
3925 struct dir_ent *tmp = dir_ent;
3926
3927 /*
3928 * delete sub-directory, this is by definition
3929 * empty
3930 */
3931 free(dir_ent->dir->pathname);
3932 free(dir_ent->dir->subpath);
3933 free(dir_ent->dir);
3934
3935 /* remove dir_ent from list */
3936 dir_ent = dir_ent->next;
3937 if(prev)
3938 prev->next = dir_ent;
3939 else
3940 dir->list = dir_ent;
3941
3942 /* free it */
3943 free_dir_entry(tmp);
3944
3945 /* update counts */
3946 dir->directory_count --;
3947 dir->count --;
3948 add_excluded(dir);
3949 continue;
3950 }
3951 }
3952
3953 prev = dir_ent;
3954 dir_ent = dir_ent->next;
3955 }
3956 }
3957
3958
3959 /*
3960 * dir_scan6 routines...
3961 * This sorts every directory and computes the inode numbers
3962 */
3963
3964 /*
3965 * Bottom up linked list merge sort.
3966 *
3967 * Qsort and other O(n log n) algorithms work well with arrays but not
3968 * linked lists. Merge sort another O(n log n) sort algorithm on the other hand
3969 * is not ideal for arrays (as it needs an additonal n storage locations
3970 * as sorting is not done in place), but it is ideal for linked lists because
3971 * it doesn't require any extra storage,
3972 */
sort_directory(struct dir_info * dir)3973 void sort_directory(struct dir_info *dir)
3974 {
3975 struct dir_ent *cur, *l1, *l2, *next;
3976 int len1, len2, stride = 1;
3977
3978 if(dir->list == NULL || dir->count < 2)
3979 return;
3980
3981 /*
3982 * We can consider our linked-list to be made up of stride length
3983 * sublists. Eacn iteration around this loop merges adjacent
3984 * stride length sublists into larger 2*stride sublists. We stop
3985 * when stride becomes equal to the entire list.
3986 *
3987 * Initially stride = 1 (by definition a sublist of 1 is sorted), and
3988 * these 1 element sublists are merged into 2 element sublists, which
3989 * are then merged into 4 element sublists and so on.
3990 */
3991 do {
3992 l2 = dir->list; /* head of current linked list */
3993 cur = NULL; /* empty output list */
3994
3995 /*
3996 * Iterate through the linked list, merging adjacent sublists.
3997 * On each interation l2 points to the next sublist pair to be
3998 * merged (if there's only one sublist left this is simply added
3999 * to the output list)
4000 */
4001 while(l2) {
4002 l1 = l2;
4003 for(len1 = 0; l2 && len1 < stride; len1 ++, l2 = l2->next);
4004 len2 = stride;
4005
4006 /*
4007 * l1 points to first sublist.
4008 * l2 points to second sublist.
4009 * Merge them onto the output list
4010 */
4011 while(len1 && l2 && len2) {
4012 if(strcmp(l1->name, l2->name) <= 0) {
4013 next = l1;
4014 l1 = l1->next;
4015 len1 --;
4016 } else {
4017 next = l2;
4018 l2 = l2->next;
4019 len2 --;
4020 }
4021
4022 if(cur) {
4023 cur->next = next;
4024 cur = next;
4025 } else
4026 dir->list = cur = next;
4027 }
4028 /*
4029 * One sublist is now empty, copy the other one onto the
4030 * output list
4031 */
4032 for(; len1; len1 --, l1 = l1->next) {
4033 if(cur) {
4034 cur->next = l1;
4035 cur = l1;
4036 } else
4037 dir->list = cur = l1;
4038 }
4039 for(; l2 && len2; len2 --, l2 = l2->next) {
4040 if(cur) {
4041 cur->next = l2;
4042 cur = l2;
4043 } else
4044 dir->list = cur = l2;
4045 }
4046 }
4047 cur->next = NULL;
4048 stride = stride << 1;
4049 } while(stride < dir->count);
4050 }
4051
4052
dir_scan6(struct dir_info * dir)4053 void dir_scan6(struct dir_info *dir)
4054 {
4055 struct dir_ent *dir_ent;
4056 unsigned int byte_count = 0;
4057
4058 sort_directory(dir);
4059
4060 for(dir_ent = dir->list; dir_ent; dir_ent = dir_ent->next) {
4061 byte_count += strlen(dir_ent->name) +
4062 sizeof(struct squashfs_dir_entry);
4063
4064 if(dir_ent->inode->root_entry)
4065 continue;
4066
4067 alloc_inode_no(dir_ent->inode, 0);
4068
4069 if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
4070 dir_scan6(dir_ent->dir);
4071 }
4072
4073 if((dir->count < 257 && byte_count < SQUASHFS_METADATA_SIZE))
4074 dir->dir_is_ldir = FALSE;
4075 }
4076
4077
4078 /*
4079 * dir_scan6 routines...
4080 * This generates the filesystem metadata and writes it out to the destination
4081 */
scan7_init_dir(struct directory * dir)4082 void scan7_init_dir(struct directory *dir)
4083 {
4084 dir->buff = malloc(SQUASHFS_METADATA_SIZE);
4085 if(dir->buff == NULL)
4086 MEM_ERROR();
4087
4088 dir->size = SQUASHFS_METADATA_SIZE;
4089 dir->p = dir->index_count_p = dir->buff;
4090 dir->entry_count = 256;
4091 dir->entry_count_p = NULL;
4092 dir->index = NULL;
4093 dir->i_count = dir->i_size = 0;
4094 }
4095
4096
scan7_readdir(struct directory * dir,struct dir_info * dir_info,struct dir_ent * dir_ent)4097 struct dir_ent *scan7_readdir(struct directory *dir, struct dir_info *dir_info,
4098 struct dir_ent *dir_ent)
4099 {
4100 if (dir_ent == NULL)
4101 dir_ent = dir_info->list;
4102 else
4103 dir_ent = dir_ent->next;
4104
4105 for(; dir_ent && dir_ent->inode->root_entry; dir_ent = dir_ent->next)
4106 add_dir(dir_ent->inode->inode, dir_ent->inode->inode_number,
4107 dir_ent->name, dir_ent->inode->type, dir);
4108
4109 return dir_ent;
4110 }
4111
4112
scan7_freedir(struct directory * dir)4113 void scan7_freedir(struct directory *dir)
4114 {
4115 if(dir->index)
4116 free(dir->index);
4117 free(dir->buff);
4118 }
4119
4120
dir_scan7(squashfs_inode * inode,struct dir_info * dir_info)4121 void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info)
4122 {
4123 int squashfs_type;
4124 int duplicate_file;
4125 struct directory dir;
4126 struct dir_ent *dir_ent = NULL;
4127
4128 scan7_init_dir(&dir);
4129
4130 while((dir_ent = scan7_readdir(&dir, dir_info, dir_ent)) != NULL) {
4131 struct stat *buf = &dir_ent->inode->buf;
4132
4133 update_info(dir_ent);
4134
4135 if(dir_ent->inode->inode == SQUASHFS_INVALID_BLK) {
4136 switch(buf->st_mode & S_IFMT) {
4137 case S_IFREG:
4138 squashfs_type = SQUASHFS_FILE_TYPE;
4139 write_file(inode, dir_ent,
4140 &duplicate_file);
4141 INFO("file %s, uncompressed size %lld "
4142 "bytes %s\n",
4143 subpathname(dir_ent),
4144 (long long) buf->st_size,
4145 duplicate_file ? "DUPLICATE" :
4146 "");
4147 break;
4148
4149 case S_IFDIR:
4150 squashfs_type = SQUASHFS_DIR_TYPE;
4151 dir_scan7(inode, dir_ent->dir);
4152 break;
4153
4154 case S_IFLNK:
4155 squashfs_type = SQUASHFS_SYMLINK_TYPE;
4156 create_inode(inode, NULL, dir_ent,
4157 squashfs_type, 0, 0, 0, NULL,
4158 NULL, NULL, 0);
4159 INFO("symbolic link %s inode 0x%llx\n",
4160 subpathname(dir_ent), *inode);
4161 sym_count ++;
4162 break;
4163
4164 case S_IFCHR:
4165 squashfs_type = SQUASHFS_CHRDEV_TYPE;
4166 create_inode(inode, NULL, dir_ent,
4167 squashfs_type, 0, 0, 0, NULL,
4168 NULL, NULL, 0);
4169 INFO("character device %s inode 0x%llx"
4170 "\n", subpathname(dir_ent),
4171 *inode);
4172 dev_count ++;
4173 break;
4174
4175 case S_IFBLK:
4176 squashfs_type = SQUASHFS_BLKDEV_TYPE;
4177 create_inode(inode, NULL, dir_ent,
4178 squashfs_type, 0, 0, 0, NULL,
4179 NULL, NULL, 0);
4180 INFO("block device %s inode 0x%llx\n",
4181 subpathname(dir_ent), *inode);
4182 dev_count ++;
4183 break;
4184
4185 case S_IFIFO:
4186 squashfs_type = SQUASHFS_FIFO_TYPE;
4187 create_inode(inode, NULL, dir_ent,
4188 squashfs_type, 0, 0, 0, NULL,
4189 NULL, NULL, 0);
4190 INFO("fifo %s inode 0x%llx\n",
4191 subpathname(dir_ent), *inode);
4192 fifo_count ++;
4193 break;
4194
4195 case S_IFSOCK:
4196 squashfs_type = SQUASHFS_SOCKET_TYPE;
4197 create_inode(inode, NULL, dir_ent,
4198 squashfs_type, 0, 0, 0, NULL,
4199 NULL, NULL, 0);
4200 INFO("unix domain socket %s inode "
4201 "0x%llx\n",
4202 subpathname(dir_ent), *inode);
4203 sock_count ++;
4204 break;
4205
4206 default:
4207 BAD_ERROR("%s unrecognised file type, "
4208 "mode is %x\n",
4209 subpathname(dir_ent),
4210 buf->st_mode);
4211 }
4212 dir_ent->inode->inode = *inode;
4213 dir_ent->inode->type = squashfs_type;
4214 } else {
4215 *inode = dir_ent->inode->inode;
4216 squashfs_type = dir_ent->inode->type;
4217 switch(squashfs_type) {
4218 case SQUASHFS_FILE_TYPE:
4219 if(!sorted)
4220 INFO("file %s, uncompressed "
4221 "size %lld bytes LINK"
4222 "\n",
4223 subpathname(dir_ent),
4224 (long long)
4225 buf->st_size);
4226 break;
4227 case SQUASHFS_SYMLINK_TYPE:
4228 INFO("symbolic link %s inode 0x%llx "
4229 "LINK\n", subpathname(dir_ent),
4230 *inode);
4231 break;
4232 case SQUASHFS_CHRDEV_TYPE:
4233 INFO("character device %s inode 0x%llx "
4234 "LINK\n", subpathname(dir_ent),
4235 *inode);
4236 break;
4237 case SQUASHFS_BLKDEV_TYPE:
4238 INFO("block device %s inode 0x%llx "
4239 "LINK\n", subpathname(dir_ent),
4240 *inode);
4241 break;
4242 case SQUASHFS_FIFO_TYPE:
4243 INFO("fifo %s inode 0x%llx LINK\n",
4244 subpathname(dir_ent), *inode);
4245 break;
4246 case SQUASHFS_SOCKET_TYPE:
4247 INFO("unix domain socket %s inode "
4248 "0x%llx LINK\n",
4249 subpathname(dir_ent), *inode);
4250 break;
4251 }
4252 }
4253
4254 add_dir(*inode, get_inode_no(dir_ent->inode), dir_ent->name,
4255 squashfs_type, &dir);
4256 }
4257
4258 write_dir(inode, dir_info, &dir);
4259 INFO("directory %s inode 0x%llx\n", subpathname(dir_info->dir_ent),
4260 *inode);
4261
4262 scan7_freedir(&dir);
4263 }
4264
4265
slog(unsigned int block)4266 unsigned int slog(unsigned int block)
4267 {
4268 int i;
4269
4270 for(i = 12; i <= 20; i++)
4271 if(block == (1 << i))
4272 return i;
4273 return 0;
4274 }
4275
4276
old_excluded(char * filename,struct stat * buf)4277 int old_excluded(char *filename, struct stat *buf)
4278 {
4279 int i;
4280
4281 for(i = 0; i < exclude; i++)
4282 if((exclude_paths[i].st_dev == buf->st_dev) &&
4283 (exclude_paths[i].st_ino == buf->st_ino))
4284 return TRUE;
4285 return FALSE;
4286 }
4287
4288
4289 #define ADD_ENTRY(buf) \
4290 if(exclude % EXCLUDE_SIZE == 0) { \
4291 exclude_paths = realloc(exclude_paths, (exclude + EXCLUDE_SIZE) \
4292 * sizeof(struct exclude_info)); \
4293 if(exclude_paths == NULL) \
4294 MEM_ERROR(); \
4295 } \
4296 exclude_paths[exclude].st_dev = buf.st_dev; \
4297 exclude_paths[exclude++].st_ino = buf.st_ino;
old_add_exclude(char * path)4298 int old_add_exclude(char *path)
4299 {
4300 int i;
4301 char *filename;
4302 struct stat buf;
4303
4304 if(path[0] == '/' || strncmp(path, "./", 2) == 0 ||
4305 strncmp(path, "../", 3) == 0) {
4306 if(lstat(path, &buf) == -1) {
4307 ERROR_START("Cannot stat exclude dir/file %s because "
4308 "%s", path, strerror(errno));
4309 ERROR_EXIT(", ignoring\n");
4310 return TRUE;
4311 }
4312 ADD_ENTRY(buf);
4313 return TRUE;
4314 }
4315
4316 for(i = 0; i < source; i++) {
4317 int res = asprintf(&filename, "%s/%s", source_path[i], path);
4318 if(res == -1)
4319 BAD_ERROR("asprintf failed in old_add_exclude\n");
4320 if(lstat(filename, &buf) == -1) {
4321 if(!(errno == ENOENT || errno == ENOTDIR)) {
4322 ERROR_START("Cannot stat exclude dir/file %s "
4323 "because %s", filename, strerror(errno));
4324 ERROR_EXIT(", ignoring\n");
4325 }
4326 free(filename);
4327 continue;
4328 }
4329 free(filename);
4330 ADD_ENTRY(buf);
4331 }
4332 return TRUE;
4333 }
4334
4335
add_old_root_entry(char * name,squashfs_inode inode,int inode_number,int type)4336 void add_old_root_entry(char *name, squashfs_inode inode, int inode_number,
4337 int type)
4338 {
4339 old_root_entry = realloc(old_root_entry,
4340 sizeof(struct old_root_entry_info) * (old_root_entries + 1));
4341 if(old_root_entry == NULL)
4342 MEM_ERROR();
4343
4344 old_root_entry[old_root_entries].name = strdup(name);
4345 old_root_entry[old_root_entries].inode.inode = inode;
4346 old_root_entry[old_root_entries].inode.inode_number = inode_number;
4347 old_root_entry[old_root_entries].inode.type = type;
4348 old_root_entry[old_root_entries++].inode.root_entry = TRUE;
4349 }
4350
4351
initialise_threads(int readq,int fragq,int bwriteq,int fwriteq,int freelst,char * destination_file)4352 void initialise_threads(int readq, int fragq, int bwriteq, int fwriteq,
4353 int freelst, char *destination_file)
4354 {
4355 int i;
4356 sigset_t sigmask, old_mask;
4357 int total_mem = readq;
4358 int reader_size;
4359 int fragment_size;
4360 int fwriter_size;
4361 /*
4362 * bwriter_size is global because it is needed in
4363 * write_file_blocks_dup()
4364 */
4365
4366 /*
4367 * Never allow the total size of the queues to be larger than
4368 * physical memory
4369 *
4370 * When adding together the possibly user supplied values, make
4371 * sure they've not been deliberately contrived to overflow an int
4372 */
4373 if(add_overflow(total_mem, fragq))
4374 BAD_ERROR("Queue sizes rediculously too large\n");
4375 total_mem += fragq;
4376 if(add_overflow(total_mem, bwriteq))
4377 BAD_ERROR("Queue sizes rediculously too large\n");
4378 total_mem += bwriteq;
4379 if(add_overflow(total_mem, fwriteq))
4380 BAD_ERROR("Queue sizes rediculously too large\n");
4381 total_mem += fwriteq;
4382
4383 check_usable_phys_mem(total_mem);
4384
4385 /*
4386 * convert from queue size in Mbytes to queue size in
4387 * blocks.
4388 *
4389 * This isn't going to overflow an int unless there exists
4390 * systems with more than 8 Petabytes of RAM!
4391 */
4392 reader_size = readq << (20 - block_log);
4393 fragment_size = fragq << (20 - block_log);
4394 bwriter_size = bwriteq << (20 - block_log);
4395 fwriter_size = fwriteq << (20 - block_log);
4396
4397 /*
4398 * setup signal handlers for the main thread, these cleanup
4399 * deleting the destination file, if appending the
4400 * handlers for SIGTERM and SIGINT will be replaced with handlers
4401 * allowing the user to press ^C twice to restore the existing
4402 * filesystem.
4403 *
4404 * SIGUSR1 is an internal signal, which is used by the sub-threads
4405 * to tell the main thread to terminate, deleting the destination file,
4406 * or if necessary restoring the filesystem on appending
4407 */
4408 signal(SIGTERM, sighandler);
4409 signal(SIGINT, sighandler);
4410 signal(SIGUSR1, sighandler);
4411
4412 /* block SIGQUIT and SIGHUP, these are handled by the info thread */
4413 sigemptyset(&sigmask);
4414 sigaddset(&sigmask, SIGQUIT);
4415 sigaddset(&sigmask, SIGHUP);
4416 sigaddset(&sigmask, SIGALRM);
4417 if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1)
4418 BAD_ERROR("Failed to set signal mask in intialise_threads\n");
4419
4420 /*
4421 * temporarily block these signals, so the created sub-threads
4422 * will ignore them, ensuring the main thread handles them
4423 */
4424 sigemptyset(&sigmask);
4425 sigaddset(&sigmask, SIGINT);
4426 sigaddset(&sigmask, SIGTERM);
4427 sigaddset(&sigmask, SIGUSR1);
4428 if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) == -1)
4429 BAD_ERROR("Failed to set signal mask in intialise_threads\n");
4430
4431 if(processors == -1) {
4432 #ifndef linux
4433 int mib[2];
4434 size_t len = sizeof(processors);
4435
4436 mib[0] = CTL_HW;
4437 #ifdef HW_AVAILCPU
4438 mib[1] = HW_AVAILCPU;
4439 #else
4440 mib[1] = HW_NCPU;
4441 #endif
4442
4443 if(sysctl(mib, 2, &processors, &len, NULL, 0) == -1) {
4444 ERROR_START("Failed to get number of available "
4445 "processors.");
4446 ERROR_EXIT(" Defaulting to 1\n");
4447 processors = 1;
4448 }
4449 #else
4450 processors = sysconf(_SC_NPROCESSORS_ONLN);
4451 #endif
4452 }
4453
4454 if(multiply_overflow(processors, 3) ||
4455 multiply_overflow(processors * 3, sizeof(pthread_t)))
4456 BAD_ERROR("Processors too large\n");
4457
4458 deflator_thread = malloc(processors * 3 * sizeof(pthread_t));
4459 if(deflator_thread == NULL)
4460 MEM_ERROR();
4461
4462 frag_deflator_thread = &deflator_thread[processors];
4463 frag_thread = &frag_deflator_thread[processors];
4464
4465 to_reader = queue_init(1);
4466 to_deflate = queue_init(reader_size);
4467 to_process_frag = queue_init(reader_size);
4468 to_writer = queue_init(bwriter_size + fwriter_size);
4469 from_writer = queue_init(1);
4470 to_frag = queue_init(fragment_size);
4471 locked_fragment = queue_init(fragment_size);
4472 to_main = seq_queue_init();
4473 reader_buffer = cache_init(block_size, reader_size, 0, 0);
4474 bwriter_buffer = cache_init(block_size, bwriter_size, 1, freelst);
4475 fwriter_buffer = cache_init(block_size, fwriter_size, 1, freelst);
4476 fragment_buffer = cache_init(block_size, fragment_size, 1, 0);
4477 reserve_cache = cache_init(block_size, processors + 1, 1, 0);
4478 pthread_create(&reader_thread, NULL, reader, NULL);
4479 pthread_create(&writer_thread, NULL, writer, NULL);
4480 init_progress_bar();
4481 init_info();
4482
4483 for(i = 0; i < processors; i++) {
4484 if(pthread_create(&deflator_thread[i], NULL, deflator, NULL))
4485 BAD_ERROR("Failed to create thread\n");
4486 if(pthread_create(&frag_deflator_thread[i], NULL, frag_deflator,
4487 NULL) != 0)
4488 BAD_ERROR("Failed to create thread\n");
4489 if(pthread_create(&frag_thread[i], NULL, frag_thrd,
4490 (void *) destination_file) != 0)
4491 BAD_ERROR("Failed to create thread\n");
4492 }
4493
4494 main_thread = pthread_self();
4495
4496 printf("Parallel mksquashfs: Using %d processor%s\n", processors,
4497 processors == 1 ? "" : "s");
4498
4499 /* Restore the signal mask for the main thread */
4500 if(pthread_sigmask(SIG_SETMASK, &old_mask, NULL) == -1)
4501 BAD_ERROR("Failed to set signal mask in intialise_threads\n");
4502 }
4503
4504
write_inode_lookup_table()4505 long long write_inode_lookup_table()
4506 {
4507 int i, inode_number, lookup_bytes = SQUASHFS_LOOKUP_BYTES(inode_count);
4508 void *it;
4509
4510 if(inode_count == sinode_count)
4511 goto skip_inode_hash_table;
4512
4513 it = realloc(inode_lookup_table, lookup_bytes);
4514 if(it == NULL)
4515 MEM_ERROR();
4516 inode_lookup_table = it;
4517
4518 for(i = 0; i < INODE_HASH_SIZE; i ++) {
4519 struct inode_info *inode;
4520
4521 for(inode = inode_info[i]; inode; inode = inode->next) {
4522
4523 inode_number = get_inode_no(inode);
4524
4525 /* The empty action will produce orphaned inode
4526 * entries in the inode_info[] table. These
4527 * entries because they are orphaned will not be
4528 * allocated an inode number in dir_scan5(), so
4529 * skip any entries with the default dummy inode
4530 * number of 0 */
4531 if(inode_number == 0)
4532 continue;
4533
4534 SQUASHFS_SWAP_LONG_LONGS(&inode->inode,
4535 &inode_lookup_table[inode_number - 1], 1);
4536
4537 }
4538 }
4539
4540 skip_inode_hash_table:
4541 return generic_write_table(lookup_bytes, inode_lookup_table, 0, NULL,
4542 noI);
4543 }
4544
4545
get_component(char * target,char ** targname)4546 char *get_component(char *target, char **targname)
4547 {
4548 char *start;
4549
4550 while(*target == '/')
4551 target ++;
4552
4553 start = target;
4554 while(*target != '/' && *target != '\0')
4555 target ++;
4556
4557 *targname = strndup(start, target - start);
4558
4559 while(*target == '/')
4560 target ++;
4561
4562 return target;
4563 }
4564
4565
free_path(struct pathname * paths)4566 void free_path(struct pathname *paths)
4567 {
4568 int i;
4569
4570 for(i = 0; i < paths->names; i++) {
4571 if(paths->name[i].paths)
4572 free_path(paths->name[i].paths);
4573 free(paths->name[i].name);
4574 if(paths->name[i].preg) {
4575 regfree(paths->name[i].preg);
4576 free(paths->name[i].preg);
4577 }
4578 }
4579
4580 free(paths);
4581 }
4582
4583
add_path(struct pathname * paths,char * target,char * alltarget)4584 struct pathname *add_path(struct pathname *paths, char *target, char *alltarget)
4585 {
4586 char *targname;
4587 int i, error;
4588
4589 target = get_component(target, &targname);
4590
4591 if(paths == NULL) {
4592 paths = malloc(sizeof(struct pathname));
4593 if(paths == NULL)
4594 MEM_ERROR();
4595
4596 paths->names = 0;
4597 paths->name = NULL;
4598 }
4599
4600 for(i = 0; i < paths->names; i++)
4601 if(strcmp(paths->name[i].name, targname) == 0)
4602 break;
4603
4604 if(i == paths->names) {
4605 /* allocate new name entry */
4606 paths->names ++;
4607 paths->name = realloc(paths->name, (i + 1) *
4608 sizeof(struct path_entry));
4609 if(paths->name == NULL)
4610 MEM_ERROR();
4611 paths->name[i].name = targname;
4612 paths->name[i].paths = NULL;
4613 if(use_regex) {
4614 paths->name[i].preg = malloc(sizeof(regex_t));
4615 if(paths->name[i].preg == NULL)
4616 MEM_ERROR();
4617 error = regcomp(paths->name[i].preg, targname,
4618 REG_EXTENDED|REG_NOSUB);
4619 if(error) {
4620 char str[1024]; /* overflow safe */
4621
4622 regerror(error, paths->name[i].preg, str, 1024);
4623 BAD_ERROR("invalid regex %s in export %s, "
4624 "because %s\n", targname, alltarget,
4625 str);
4626 }
4627 } else
4628 paths->name[i].preg = NULL;
4629
4630 if(target[0] == '\0')
4631 /* at leaf pathname component */
4632 paths->name[i].paths = NULL;
4633 else
4634 /* recurse adding child components */
4635 paths->name[i].paths = add_path(NULL, target,
4636 alltarget);
4637 } else {
4638 /* existing matching entry */
4639 free(targname);
4640
4641 if(paths->name[i].paths == NULL) {
4642 /* No sub-directory which means this is the leaf
4643 * component of a pre-existing exclude which subsumes
4644 * the exclude currently being added, in which case stop
4645 * adding components */
4646 } else if(target[0] == '\0') {
4647 /* at leaf pathname component and child components exist
4648 * from more specific excludes, delete as they're
4649 * subsumed by this exclude */
4650 free_path(paths->name[i].paths);
4651 paths->name[i].paths = NULL;
4652 } else
4653 /* recurse adding child components */
4654 add_path(paths->name[i].paths, target, alltarget);
4655 }
4656
4657 return paths;
4658 }
4659
4660
add_exclude(char * target)4661 void add_exclude(char *target)
4662 {
4663
4664 if(target[0] == '/' || strncmp(target, "./", 2) == 0 ||
4665 strncmp(target, "../", 3) == 0)
4666 BAD_ERROR("/, ./ and ../ prefixed excludes not supported with "
4667 "-wildcards or -regex options\n");
4668 else if(strncmp(target, "... ", 4) == 0)
4669 stickypath = add_path(stickypath, target + 4, target + 4);
4670 else
4671 path = add_path(path, target, target);
4672 }
4673
4674
display_path(int depth,struct pathname * paths)4675 void display_path(int depth, struct pathname *paths)
4676 {
4677 int i, n;
4678
4679 if(paths == NULL)
4680 return;
4681
4682 for(i = 0; i < paths->names; i++) {
4683 for(n = 0; n < depth; n++)
4684 printf("\t");
4685 printf("%d: %s\n", depth, paths->name[i].name);
4686 display_path(depth + 1, paths->name[i].paths);
4687 }
4688 }
4689
4690
display_path2(struct pathname * paths,char * string)4691 void display_path2(struct pathname *paths, char *string)
4692 {
4693 int i;
4694 char *path;
4695
4696 if(paths == NULL) {
4697 printf("%s\n", string);
4698 return;
4699 }
4700
4701 for(i = 0; i < paths->names; i++) {
4702 int res = asprintf(&path, "%s/%s", string, paths->name[i].name);
4703 if(res == -1)
4704 BAD_ERROR("asprintf failed in display_path2\n");
4705 display_path2(paths->name[i].paths, path);
4706 free(path);
4707 }
4708 }
4709
4710
add_subdir(struct pathnames * paths,struct pathname * path)4711 struct pathnames *add_subdir(struct pathnames *paths, struct pathname *path)
4712 {
4713 int count = paths == NULL ? 0 : paths->count;
4714
4715 if(count % PATHS_ALLOC_SIZE == 0) {
4716 paths = realloc(paths, sizeof(struct pathnames) +
4717 (count + PATHS_ALLOC_SIZE) * sizeof(struct pathname *));
4718 if(paths == NULL)
4719 MEM_ERROR();
4720 }
4721
4722 paths->path[count] = path;
4723 paths->count = count + 1;
4724 return paths;
4725 }
4726
4727
excluded_match(char * name,struct pathname * path,struct pathnames ** new)4728 int excluded_match(char *name, struct pathname *path, struct pathnames **new)
4729 {
4730 int i;
4731
4732 for(i = 0; i < path->names; i++) {
4733 int match = use_regex ?
4734 regexec(path->name[i].preg, name, (size_t) 0,
4735 NULL, 0) == 0 :
4736 fnmatch(path->name[i].name, name,
4737 FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;
4738
4739 if(match) {
4740 if(path->name[i].paths == NULL || new == NULL)
4741 /* match on a leaf component, any subdirectories
4742 * in the filesystem should be excluded */
4743 return TRUE;
4744 else
4745 /* match on a non-leaf component, add any
4746 * subdirectories to the new set of
4747 * subdirectories to scan for this name */
4748 *new = add_subdir(*new, path->name[i].paths);
4749 }
4750 }
4751
4752 return FALSE;
4753 }
4754
4755
excluded(char * name,struct pathnames * paths,struct pathnames ** new)4756 int excluded(char *name, struct pathnames *paths, struct pathnames **new)
4757 {
4758 int n;
4759
4760 if(stickypath && excluded_match(name, stickypath, NULL))
4761 return TRUE;
4762
4763 for(n = 0; paths && n < paths->count; n++) {
4764 int res = excluded_match(name, paths->path[n], new);
4765 if(res) {
4766 free(*new);
4767 *new = NULL;
4768 return TRUE;
4769 }
4770 }
4771
4772 /*
4773 * Either:
4774 * - no matching names found, return empty new search set, or
4775 * - one or more matches with sub-directories found (no leaf matches),
4776 * in which case return new search set.
4777 *
4778 * In either case return FALSE as we don't want to exclude this entry
4779 */
4780 return FALSE;
4781 }
4782
4783
process_exclude_file(char * argv)4784 void process_exclude_file(char *argv)
4785 {
4786 FILE *fd;
4787 char buffer[MAX_LINE + 1]; /* overflow safe */
4788 char *filename;
4789
4790 fd = fopen(argv, "r");
4791 if(fd == NULL)
4792 BAD_ERROR("Failed to open exclude file \"%s\" because %s\n",
4793 argv, strerror(errno));
4794
4795 while(fgets(filename = buffer, MAX_LINE + 1, fd) != NULL) {
4796 int len = strlen(filename);
4797
4798 if(len == MAX_LINE && filename[len - 1] != '\n')
4799 /* line too large */
4800 BAD_ERROR("Line too long when reading "
4801 "exclude file \"%s\", larger than %d "
4802 "bytes\n", argv, MAX_LINE);
4803
4804 /*
4805 * Remove '\n' terminator if it exists (the last line
4806 * in the file may not be '\n' terminated)
4807 */
4808 if(len && filename[len - 1] == '\n')
4809 filename[len - 1] = '\0';
4810
4811 /* Skip any leading whitespace */
4812 while(isspace(*filename))
4813 filename ++;
4814
4815 /* if comment line, skip */
4816 if(*filename == '#')
4817 continue;
4818
4819 /*
4820 * check for initial backslash, to accommodate
4821 * filenames with leading space or leading # character
4822 */
4823 if(*filename == '\\')
4824 filename ++;
4825
4826 /* if line is now empty after skipping characters, skip it */
4827 if(*filename == '\0')
4828 continue;
4829
4830 if(old_exclude)
4831 old_add_exclude(filename);
4832 else
4833 add_exclude(filename);
4834 }
4835
4836 if(ferror(fd))
4837 BAD_ERROR("Reading exclude file \"%s\" failed because %s\n",
4838 argv, strerror(errno));
4839
4840 fclose(fd);
4841 }
4842
4843 /* ANDROID CHANGES START*/
4844 #ifdef ANDROID
4845 /*
4846 * Return TRUE (don't compress) if the (regular) file is in the
4847 * whitelist. Else return the Global noD value.
4848 *
4849 * Note : These functions are lifted 100% from the existing exclude
4850 * file code. For maintainability, I've kept this code separate from
4851 * the exclude code instead of having common code for both paths.
4852 */
4853 static int
whitelisted(struct stat * buf)4854 whitelisted(struct stat *buf)
4855 {
4856 int i;
4857
4858 /*
4859 * only regular files in the whitelist
4860 */
4861 if (!S_ISREG(buf->st_mode))
4862 return noD;
4863 for (i = 0; i < whitelist; i++) {
4864 if ((whitelist_paths[i].st_dev == buf->st_dev) &&
4865 (whitelist_paths[i].st_ino == buf->st_ino)) {
4866 /* Don't compress */
4867 whitelisted_count++;
4868 return TRUE;
4869 }
4870 }
4871 return noD;
4872 }
4873
4874 static void
add_whitelist_entry(char * filename,struct stat * buf)4875 add_whitelist_entry(char *filename, struct stat *buf)
4876 {
4877 if (!S_ISREG(buf->st_mode)) {
4878 BAD_ERROR("Cannot whitelist %s only regular files can be whitelisted",
4879 filename);
4880 }
4881 if (whitelist % WHITELIST_SIZE == 0) {
4882 whitelist_paths = realloc(whitelist_paths,
4883 (whitelist + WHITELIST_SIZE)
4884 * sizeof(struct whitelist_info));
4885 if (whitelist_paths == NULL)
4886 MEM_ERROR();
4887 }
4888 whitelist_paths[whitelist].st_dev = buf->st_dev;
4889 whitelist_paths[whitelist++].st_ino = buf->st_ino;
4890 }
4891
4892 static int
add_whitelist(char * path)4893 add_whitelist(char *path)
4894 {
4895 int i;
4896 char *filename;
4897 struct stat buf;
4898
4899 /* Absolute of (filesystem) relative path */
4900 if (path[0] == '/' || strncmp(path, "./", 2) == 0 ||
4901 strncmp(path, "../", 3) == 0) {
4902 if(lstat(path, &buf) == -1) {
4903 BAD_ERROR("Cannot stat whitelist dir/file %s because "
4904 "%s", path, strerror(errno));
4905 }
4906 add_whitelist_entry(path, &buf);
4907 return TRUE;
4908 }
4909
4910 /* pathname relative to mksquashfs source dirs */
4911 for(i = 0; i < source; i++) {
4912 int res = asprintf(&filename, "%s/%s", source_path[i], path);
4913 if(res == -1)
4914 BAD_ERROR("asprintf failed in add_whitelist\n");
4915 if(lstat(filename, &buf) == -1) {
4916 if(!(errno == ENOENT || errno == ENOTDIR)) {
4917 BAD_ERROR("Cannot stat whitelist dir/file %s "
4918 "because %s", filename, strerror(errno));
4919 }
4920 free(filename);
4921 continue;
4922 }
4923 add_whitelist_entry(filename, &buf);
4924 free(filename);
4925 }
4926 return TRUE;
4927 }
4928
4929 static void
process_whitelist_file(char * argv)4930 process_whitelist_file(char *argv)
4931 {
4932 FILE *fd;
4933 char buffer[MAX_LINE + 1]; /* overflow safe */
4934 char *filename;
4935
4936 fd = fopen(argv, "r");
4937 if(fd == NULL)
4938 BAD_ERROR("Failed to open whitelist file \"%s\" because %s\n",
4939 argv, strerror(errno));
4940
4941 while(fgets(filename = buffer, MAX_LINE + 1, fd) != NULL) {
4942 int len = strlen(filename);
4943
4944 if(len == MAX_LINE && filename[len - 1] != '\n')
4945 /* line too large */
4946 BAD_ERROR("Line too long when reading "
4947 "whitelist file \"%s\", larger than %d "
4948 "bytes\n", argv, MAX_LINE);
4949
4950 /*
4951 * Remove '\n' terminator if it exists (the last line
4952 * in the file may not be '\n' terminated)
4953 */
4954 if(len && filename[len - 1] == '\n')
4955 filename[len - 1] = '\0';
4956
4957 /* Skip any leading whitespace */
4958 while(isspace(*filename))
4959 filename ++;
4960
4961 /* if comment line, skip */
4962 if(*filename == '#')
4963 continue;
4964
4965 /*
4966 * check for initial backslash, to accommodate
4967 * filenames with leading space or leading # character
4968 */
4969 if(*filename == '\\')
4970 filename ++;
4971
4972 /* if line is now empty after skipping characters, skip it */
4973 if(*filename == '\0')
4974 continue;
4975
4976 add_whitelist(filename);
4977 }
4978
4979 if(ferror(fd))
4980 BAD_ERROR("Reading whitelist file \"%s\" failed because %s\n",
4981 argv, strerror(errno));
4982
4983 fclose(fd);
4984 }
4985 #endif
4986 /* ANDROID CHANGES END */
4987
4988 #define RECOVER_ID "Squashfs recovery file v1.0\n"
4989 #define RECOVER_ID_SIZE 28
4990
write_recovery_data(struct squashfs_super_block * sBlk)4991 void write_recovery_data(struct squashfs_super_block *sBlk)
4992 {
4993 int res, recoverfd, bytes = sBlk->bytes_used - sBlk->inode_table_start;
4994 pid_t pid = getpid();
4995 char *metadata;
4996 char header[] = RECOVER_ID;
4997
4998 if(recover == FALSE) {
4999 printf("No recovery data option specified.\n");
5000 printf("Skipping saving recovery file.\n\n");
5001 return;
5002 }
5003
5004 metadata = malloc(bytes);
5005 if(metadata == NULL)
5006 MEM_ERROR();
5007
5008 res = read_fs_bytes(fd, sBlk->inode_table_start, bytes, metadata);
5009 if(res == 0) {
5010 ERROR("Failed to read append filesystem metadata\n");
5011 BAD_ERROR("Filesystem corrupted?\n");
5012 }
5013
5014 res = asprintf(&recovery_file, "squashfs_recovery_%s_%d",
5015 getbase(destination_file), pid);
5016 if(res == -1)
5017 MEM_ERROR();
5018
5019 recoverfd = open(recovery_file, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
5020 if(recoverfd == -1)
5021 BAD_ERROR("Failed to create recovery file, because %s. "
5022 "Aborting\n", strerror(errno));
5023
5024 if(write_bytes(recoverfd, header, RECOVER_ID_SIZE) == -1)
5025 BAD_ERROR("Failed to write recovery file, because %s\n",
5026 strerror(errno));
5027
5028 if(write_bytes(recoverfd, sBlk, sizeof(struct squashfs_super_block)) == -1)
5029 BAD_ERROR("Failed to write recovery file, because %s\n",
5030 strerror(errno));
5031
5032 if(write_bytes(recoverfd, metadata, bytes) == -1)
5033 BAD_ERROR("Failed to write recovery file, because %s\n",
5034 strerror(errno));
5035
5036 close(recoverfd);
5037 free(metadata);
5038
5039 printf("Recovery file \"%s\" written\n", recovery_file);
5040 printf("If Mksquashfs aborts abnormally (i.e. power failure), run\n");
5041 printf("mksquashfs dummy %s -recover %s\n", destination_file,
5042 recovery_file);
5043 printf("to restore filesystem\n\n");
5044 }
5045
5046
read_recovery_data(char * recovery_file,char * destination_file)5047 void read_recovery_data(char *recovery_file, char *destination_file)
5048 {
5049 int fd, recoverfd, bytes;
5050 struct squashfs_super_block orig_sBlk, sBlk;
5051 char *metadata;
5052 int res;
5053 struct stat buf;
5054 char header[] = RECOVER_ID;
5055 char header2[RECOVER_ID_SIZE];
5056
5057 recoverfd = open(recovery_file, O_RDONLY);
5058 if(recoverfd == -1)
5059 BAD_ERROR("Failed to open recovery file because %s\n",
5060 strerror(errno));
5061
5062 if(stat(destination_file, &buf) == -1)
5063 BAD_ERROR("Failed to stat destination file, because %s\n",
5064 strerror(errno));
5065
5066 fd = open(destination_file, O_RDWR);
5067 if(fd == -1)
5068 BAD_ERROR("Failed to open destination file because %s\n",
5069 strerror(errno));
5070
5071 res = read_bytes(recoverfd, header2, RECOVER_ID_SIZE);
5072 if(res == -1)
5073 BAD_ERROR("Failed to read recovery file, because %s\n",
5074 strerror(errno));
5075 if(res < RECOVER_ID_SIZE)
5076 BAD_ERROR("Recovery file appears to be truncated\n");
5077 if(strncmp(header, header2, RECOVER_ID_SIZE) !=0 )
5078 BAD_ERROR("Not a recovery file\n");
5079
5080 res = read_bytes(recoverfd, &sBlk, sizeof(struct squashfs_super_block));
5081 if(res == -1)
5082 BAD_ERROR("Failed to read recovery file, because %s\n",
5083 strerror(errno));
5084 if(res < sizeof(struct squashfs_super_block))
5085 BAD_ERROR("Recovery file appears to be truncated\n");
5086
5087 res = read_fs_bytes(fd, 0, sizeof(struct squashfs_super_block), &orig_sBlk);
5088 if(res == 0) {
5089 ERROR("Failed to read superblock from output filesystem\n");
5090 BAD_ERROR("Output filesystem is empty!\n");
5091 }
5092
5093 if(memcmp(((char *) &sBlk) + 4, ((char *) &orig_sBlk) + 4,
5094 sizeof(struct squashfs_super_block) - 4) != 0)
5095 BAD_ERROR("Recovery file and destination file do not seem to "
5096 "match\n");
5097
5098 bytes = sBlk.bytes_used - sBlk.inode_table_start;
5099
5100 metadata = malloc(bytes);
5101 if(metadata == NULL)
5102 MEM_ERROR();
5103
5104 res = read_bytes(recoverfd, metadata, bytes);
5105 if(res == -1)
5106 BAD_ERROR("Failed to read recovery file, because %s\n",
5107 strerror(errno));
5108 if(res < bytes)
5109 BAD_ERROR("Recovery file appears to be truncated\n");
5110
5111 write_destination(fd, 0, sizeof(struct squashfs_super_block), &sBlk);
5112
5113 write_destination(fd, sBlk.inode_table_start, bytes, metadata);
5114
5115 close(recoverfd);
5116 close(fd);
5117
5118 printf("Successfully wrote recovery file \"%s\". Exiting\n",
5119 recovery_file);
5120
5121 exit(0);
5122 }
5123
5124
write_filesystem_tables(struct squashfs_super_block * sBlk,int nopad)5125 void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad)
5126 {
5127 int i;
5128
5129 sBlk->fragments = fragments;
5130 sBlk->no_ids = id_count;
5131 sBlk->inode_table_start = write_inodes();
5132 sBlk->directory_table_start = write_directories();
5133 sBlk->fragment_table_start = write_fragment_table();
5134 sBlk->lookup_table_start = exportable ? write_inode_lookup_table() :
5135 SQUASHFS_INVALID_BLK;
5136 sBlk->id_table_start = write_id_table();
5137 sBlk->xattr_id_table_start = write_xattrs();
5138
5139 TRACE("sBlk->inode_table_start 0x%llx\n", sBlk->inode_table_start);
5140 TRACE("sBlk->directory_table_start 0x%llx\n",
5141 sBlk->directory_table_start);
5142 TRACE("sBlk->fragment_table_start 0x%llx\n", sBlk->fragment_table_start);
5143 if(exportable)
5144 TRACE("sBlk->lookup_table_start 0x%llx\n",
5145 sBlk->lookup_table_start);
5146
5147 sBlk->bytes_used = bytes;
5148
5149 sBlk->compression = comp->id;
5150
5151 SQUASHFS_INSWAP_SUPER_BLOCK(sBlk);
5152 write_destination(fd, SQUASHFS_START, sizeof(*sBlk), sBlk);
5153
5154 if(!nopad && (i = bytes & (4096 - 1))) {
5155 char temp[4096] = {0};
5156 write_destination(fd, bytes, 4096 - i, temp);
5157 }
5158
5159 close(fd);
5160
5161 if(recovery_file)
5162 unlink(recovery_file);
5163
5164 total_bytes += total_inode_bytes + total_directory_bytes +
5165 sizeof(struct squashfs_super_block) + total_xattr_bytes;
5166
5167 printf("\n%sSquashfs %d.%d filesystem, %s compressed, data block size"
5168 " %d\n", exportable ? "Exportable " : "", SQUASHFS_MAJOR,
5169 SQUASHFS_MINOR, comp->name, block_size);
5170 printf("\t%s data, %s metadata, %s fragments, %s xattrs\n",
5171 noD ? "uncompressed" : "compressed", noI ? "uncompressed" :
5172 "compressed", no_fragments ? "no" : noF ? "uncompressed" :
5173 "compressed", no_xattrs ? "no" : noX ? "uncompressed" :
5174 "compressed");
5175 printf("\tduplicates are %sremoved\n", duplicate_checking ? "" :
5176 "not ");
5177 printf("Filesystem size %.2f Kbytes (%.2f Mbytes)\n", bytes / 1024.0,
5178 bytes / (1024.0 * 1024.0));
5179 printf("\t%.2f%% of uncompressed filesystem size (%.2f Kbytes)\n",
5180 ((float) bytes / total_bytes) * 100.0, total_bytes / 1024.0);
5181 printf("Inode table size %d bytes (%.2f Kbytes)\n",
5182 inode_bytes, inode_bytes / 1024.0);
5183 printf("\t%.2f%% of uncompressed inode table size (%d bytes)\n",
5184 ((float) inode_bytes / total_inode_bytes) * 100.0,
5185 total_inode_bytes);
5186 printf("Directory table size %d bytes (%.2f Kbytes)\n",
5187 directory_bytes, directory_bytes / 1024.0);
5188 printf("\t%.2f%% of uncompressed directory table size (%d bytes)\n",
5189 ((float) directory_bytes / total_directory_bytes) * 100.0,
5190 total_directory_bytes);
5191 if(total_xattr_bytes) {
5192 printf("Xattr table size %d bytes (%.2f Kbytes)\n",
5193 xattr_bytes, xattr_bytes / 1024.0);
5194 printf("\t%.2f%% of uncompressed xattr table size (%d bytes)\n",
5195 ((float) xattr_bytes / total_xattr_bytes) * 100.0,
5196 total_xattr_bytes);
5197 }
5198 if(duplicate_checking)
5199 printf("Number of duplicate files found %d\n", file_count -
5200 dup_files);
5201 else
5202 printf("No duplicate files removed\n");
5203 printf("Number of inodes %d\n", inode_count);
5204 printf("Number of files %d\n", file_count);
5205 if(!no_fragments)
5206 printf("Number of fragments %d\n", fragments);
5207 printf("Number of symbolic links %d\n", sym_count);
5208 printf("Number of device nodes %d\n", dev_count);
5209 printf("Number of fifo nodes %d\n", fifo_count);
5210 printf("Number of socket nodes %d\n", sock_count);
5211 printf("Number of directories %d\n", dir_count);
5212 printf("Number of ids (unique uids + gids) %d\n", id_count);
5213 printf("Number of uids %d\n", uid_count);
5214
5215 for(i = 0; i < id_count; i++) {
5216 if(id_table[i]->flags & ISA_UID) {
5217 struct passwd *user = getpwuid(id_table[i]->id);
5218 printf("\t%s (%d)\n", user == NULL ? "unknown" :
5219 user->pw_name, id_table[i]->id);
5220 }
5221 }
5222
5223 printf("Number of gids %d\n", guid_count);
5224
5225 for(i = 0; i < id_count; i++) {
5226 if(id_table[i]->flags & ISA_GID) {
5227 struct group *group = getgrgid(id_table[i]->id);
5228 printf("\t%s (%d)\n", group == NULL ? "unknown" :
5229 group->gr_name, id_table[i]->id);
5230 }
5231 }
5232
5233 printf("Number of whitelisted (uncompressed) files %d\n",
5234 whitelisted_count);
5235 }
5236
5237
parse_numberll(char * start,long long * res,int size)5238 int parse_numberll(char *start, long long *res, int size)
5239 {
5240 char *end;
5241 long long number;
5242
5243 errno = 0; /* To distinguish success/failure after call */
5244
5245 number = strtoll(start, &end, 10);
5246
5247 /*
5248 * check for strtoll underflow or overflow in conversion, and other
5249 * errors.
5250 */
5251 if((errno == ERANGE && (number == LLONG_MIN || number == LLONG_MAX)) ||
5252 (errno != 0 && number == 0))
5253 return 0;
5254
5255 /* reject negative numbers as invalid */
5256 if(number < 0)
5257 return 0;
5258
5259 if(size) {
5260 /*
5261 * Check for multiplier and trailing junk.
5262 * But first check that a number exists before the
5263 * multiplier
5264 */
5265 if(end == start)
5266 return 0;
5267
5268 switch(end[0]) {
5269 case 'g':
5270 case 'G':
5271 if(multiply_overflowll(number, 1073741824))
5272 return 0;
5273 number *= 1073741824;
5274
5275 if(end[1] != '\0')
5276 /* trailing junk after multiplier, but
5277 * allow it to be "bytes" */
5278 if(strcmp(end + 1, "bytes"))
5279 return 0;
5280
5281 break;
5282 case 'm':
5283 case 'M':
5284 if(multiply_overflowll(number, 1048576))
5285 return 0;
5286 number *= 1048576;
5287
5288 if(end[1] != '\0')
5289 /* trailing junk after multiplier, but
5290 * allow it to be "bytes" */
5291 if(strcmp(end + 1, "bytes"))
5292 return 0;
5293
5294 break;
5295 case 'k':
5296 case 'K':
5297 if(multiply_overflowll(number, 1024))
5298 return 0;
5299 number *= 1024;
5300
5301 if(end[1] != '\0')
5302 /* trailing junk after multiplier, but
5303 * allow it to be "bytes" */
5304 if(strcmp(end + 1, "bytes"))
5305 return 0;
5306
5307 break;
5308 case '\0':
5309 break;
5310 default:
5311 /* trailing junk after number */
5312 return 0;
5313 }
5314 } else if(end[0] != '\0')
5315 /* trailing junk after number */
5316 return 0;
5317
5318 *res = number;
5319 return 1;
5320 }
5321
5322
parse_number(char * start,int * res,int size)5323 int parse_number(char *start, int *res, int size)
5324 {
5325 long long number;
5326
5327 if(!parse_numberll(start, &number, size))
5328 return 0;
5329
5330 /* check if long result will overflow signed int */
5331 if(number > INT_MAX)
5332 return 0;
5333
5334 *res = (int) number;
5335 return 1;
5336 }
5337
5338
parse_num(char * arg,int * res)5339 int parse_num(char *arg, int *res)
5340 {
5341 return parse_number(arg, res, 0);
5342 }
5343
5344
parse_ugid_map(char * map_str,struct ugid_map_entry ugid_mapping[UGID_ENTRIES],unsigned int * ugid_map_count)5345 int parse_ugid_map(char *map_str,
5346 struct ugid_map_entry ugid_mapping[UGID_ENTRIES],
5347 unsigned int *ugid_map_count)
5348 {
5349 char *line_state, *token_state;
5350 char *line, *line_str, *token, *token_str;
5351 long long numbers[3];
5352 int i;
5353
5354 for (*ugid_map_count = 0, line_str = map_str;;
5355 ++*ugid_map_count, line_str = NULL) {
5356 line = strtok_r(line_str, "\n", &line_state);
5357 if (line == NULL)
5358 break;
5359 ERROR("line: %s\n", line);
5360 if (*ugid_map_count >= UGID_ENTRIES) {
5361 ERROR("Too many entries for u/gid mapping\n");
5362 return -1;
5363 }
5364
5365 for (i = 0, token_str = line; i < 3; i++, token_str = NULL) {
5366 token = strtok_r(token_str, " ", &token_state);
5367 ERROR("token: %d, %s\n", i, token);
5368 if (token == NULL ||
5369 !parse_numberll(token, &numbers[i], 0) ||
5370 numbers[i] < 0 || numbers[i] > ULONG_MAX) {
5371 ERROR("Malformed u/gid mapping line1\n");
5372 return -1;
5373 }
5374 }
5375
5376 if (numbers[0] + numbers[2] > ULONG_MAX) {
5377 ERROR("u/gid mapping overflow\n");
5378 return -1;
5379 }
5380
5381 if (numbers[1] + numbers[2] > ULONG_MAX) {
5382 ERROR("u/gid mapping overflow\n");
5383 return -1;
5384 }
5385
5386 if (strtok_r(NULL, " ", &token_state) != NULL) {
5387 ERROR("Malformed u/gid mapping line2\n");
5388 return -1;
5389 }
5390
5391 ugid_mapping[*ugid_map_count].child_id =
5392 (unsigned int)numbers[0];
5393 ugid_mapping[*ugid_map_count].parent_id =
5394 (unsigned int)numbers[1];
5395 ugid_mapping[*ugid_map_count].length = (unsigned int)numbers[2];
5396 }
5397
5398 return 0;
5399 }
5400
5401
get_physical_memory()5402 int get_physical_memory()
5403 {
5404 int phys_mem;
5405 #ifndef linux
5406 #ifdef HW_MEMSIZE
5407 #define SYSCTL_PHYSMEM HW_MEMSIZE
5408 #elif defined(HW_PHYSMEM64)
5409 #define SYSCTL_PHYSMEM HW_PHYSMEM64
5410 #else
5411 #define SYSCTL_PHYSMEM HW_PHYSMEM
5412 #endif
5413
5414 int mib[2];
5415 uint64_t sysctl_physmem = 0;
5416 size_t sysctl_len = sizeof(sysctl_physmem);
5417
5418 mib[0] = CTL_HW;
5419 mib[1] = SYSCTL_PHYSMEM;
5420
5421 if(sysctl(mib, 2, &sysctl_physmem, &sysctl_len, NULL, 0) == 0) {
5422 /* some systems use 32-bit values, work with what we're given */
5423 if (sysctl_len == 4)
5424 sysctl_physmem = *(uint32_t*)&sysctl_physmem;
5425 phys_mem = sysctl_physmem >> 20;
5426 } else {
5427 ERROR_START("Failed to get amount of available "
5428 "memory.");
5429 ERROR_EXIT(" Defaulting to least viable amount\n");
5430 phys_mem = SQUASHFS_LOWMEM;
5431 }
5432 #undef SYSCTL_PHYSMEM
5433 #else
5434 /* Long longs are used here because with PAE, a 32-bit
5435 machine can have more than 4GB of physical memory */
5436
5437 long long num_pages = sysconf(_SC_PHYS_PAGES);
5438 long long page_size = sysconf(_SC_PAGESIZE);
5439 phys_mem = num_pages * page_size >> 20;
5440 if(num_pages == -1 || page_size == -1)
5441 return 0;
5442
5443 #endif
5444
5445 if(phys_mem < SQUASHFS_LOWMEM)
5446 BAD_ERROR("Mksquashfs requires more physical memory than is "
5447 "available!\n");
5448
5449 return phys_mem;
5450 }
5451
5452
check_usable_phys_mem(int total_mem)5453 void check_usable_phys_mem(int total_mem)
5454 {
5455 /*
5456 * We want to allow users to use as much of their physical
5457 * memory as they wish. However, for practical reasons there are
5458 * limits which need to be imposed, to protect users from themselves
5459 * and to prevent people from using Mksquashfs as a DOS attack by using
5460 * all physical memory. Mksquashfs uses memory to cache data from disk
5461 * to optimise performance. It is pointless to ask it to use more
5462 * than 75% of physical memory, as this causes thrashing and it is thus
5463 * self-defeating.
5464 */
5465 int mem = get_physical_memory();
5466
5467 mem = (mem >> 1) + (mem >> 2); /* 75% */
5468
5469 if(total_mem > mem && mem) {
5470 ERROR("Total memory requested is more than 75%% of physical "
5471 "memory.\n");
5472 ERROR("Mksquashfs uses memory to cache data from disk to "
5473 "optimise performance.\n");
5474 ERROR("It is pointless to ask it to use more than this amount "
5475 "of memory, as this\n");
5476 ERROR("causes thrashing and it is thus self-defeating.\n");
5477 BAD_ERROR("Requested memory size too large\n");
5478 }
5479
5480 if(sizeof(void *) == 4 && total_mem > 2048) {
5481 /*
5482 * If we're running on a kernel with PAE or on a 64-bit kernel,
5483 * then the 75% physical memory limit can still easily exceed
5484 * the addressable memory by this process.
5485 *
5486 * Due to the typical kernel/user-space split (1GB/3GB, or
5487 * 2GB/2GB), we have to conservatively assume the 32-bit
5488 * processes can only address 2-3GB. So refuse if the user
5489 * tries to allocate more than 2GB.
5490 */
5491 ERROR("Total memory requested may exceed maximum "
5492 "addressable memory by this process\n");
5493 BAD_ERROR("Requested memory size too large\n");
5494 }
5495 }
5496
5497
get_default_phys_mem()5498 int get_default_phys_mem()
5499 {
5500 /*
5501 * get_physical_memory() relies on /proc being mounted.
5502 * If it fails, issue a warning, and use
5503 * SQUASHFS_LOWMEM / SQUASHFS_TAKE as default,
5504 * and allow a larger value to be set with -mem.
5505 */
5506 int mem = get_physical_memory();
5507
5508 if(mem == 0) {
5509 mem = SQUASHFS_LOWMEM / SQUASHFS_TAKE;
5510
5511 ERROR("Warning: Cannot get size of physical memory, probably "
5512 "because /proc is missing.\n");
5513 ERROR("Warning: Defaulting to minimal use of %d Mbytes, use "
5514 "-mem to set a better value,\n", mem);
5515 ERROR("Warning: or fix /proc.\n");
5516 } else
5517 mem /= SQUASHFS_TAKE;
5518
5519 if(sizeof(void *) == 4 && mem > 640) {
5520 /*
5521 * If we're running on a kernel with PAE or on a 64-bit kernel,
5522 * the default memory usage can exceed the addressable
5523 * memory by this process.
5524 * Due to the typical kernel/user-space split (1GB/3GB, or
5525 * 2GB/2GB), we have to conservatively assume the 32-bit
5526 * processes can only address 2-3GB. So limit the default
5527 * usage to 640M, which gives room for other data.
5528 */
5529 mem = 640;
5530 }
5531
5532 return mem;
5533 }
5534
5535
calculate_queue_sizes(int mem,int * readq,int * fragq,int * bwriteq,int * fwriteq)5536 void calculate_queue_sizes(int mem, int *readq, int *fragq, int *bwriteq,
5537 int *fwriteq)
5538 {
5539 *readq = mem / SQUASHFS_READQ_MEM;
5540 *bwriteq = mem / SQUASHFS_BWRITEQ_MEM;
5541 *fwriteq = mem / SQUASHFS_FWRITEQ_MEM;
5542 *fragq = mem - *readq - *bwriteq - *fwriteq;
5543 }
5544
5545
5546 #define VERSION() \
5547 printf("mksquashfs version 4.3-git (2014/09/12)\n");\
5548 printf("copyright (C) 2014 Phillip Lougher "\
5549 "<[email protected]>\n\n"); \
5550 printf("This program is free software; you can redistribute it and/or"\
5551 "\n");\
5552 printf("modify it under the terms of the GNU General Public License"\
5553 "\n");\
5554 printf("as published by the Free Software Foundation; either version "\
5555 "2,\n");\
5556 printf("or (at your option) any later version.\n\n");\
5557 printf("This program is distributed in the hope that it will be "\
5558 "useful,\n");\
5559 printf("but WITHOUT ANY WARRANTY; without even the implied warranty "\
5560 "of\n");\
5561 printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"\
5562 "\n");\
5563 printf("GNU General Public License for more details.\n");
main(int argc,char * argv[])5564 int main(int argc, char *argv[])
5565 {
5566 struct stat buf, source_buf;
5567 int res, i;
5568 char *b, *root_name = NULL;
5569 int keep_as_directory = FALSE;
5570 squashfs_inode inode;
5571 int readq;
5572 int fragq;
5573 int bwriteq;
5574 int fwriteq;
5575 int total_mem = get_default_phys_mem();
5576 int progress = TRUE;
5577 int force_progress = FALSE;
5578 struct file_buffer **fragment = NULL;
5579 /* ANDROID CHANGES START*/
5580 #ifdef ANDROID
5581 const char *fs_config_file = NULL;
5582 #endif
5583 /* ANDROID CHANGES END */
5584
5585 if(argc > 1 && strcmp(argv[1], "-version") == 0) {
5586 VERSION();
5587 exit(0);
5588 }
5589
5590 block_log = slog(block_size);
5591 calculate_queue_sizes(total_mem, &readq, &fragq, &bwriteq, &fwriteq);
5592
5593 for(i = 1; i < argc && argv[i][0] != '-'; i++);
5594 if(i < 3)
5595 goto printOptions;
5596 source_path = argv + 1;
5597 source = i - 2;
5598
5599 /*
5600 * Scan the command line for -comp xxx option, this is to ensure
5601 * any -X compressor specific options are passed to the
5602 * correct compressor
5603 */
5604 for(; i < argc; i++) {
5605 struct compressor *prev_comp = comp;
5606
5607 if(strcmp(argv[i], "-comp") == 0) {
5608 if(++i == argc) {
5609 ERROR("%s: -comp missing compression type\n",
5610 argv[0]);
5611 exit(1);
5612 }
5613 comp = lookup_compressor(argv[i]);
5614 if(!comp->supported) {
5615 ERROR("%s: Compressor \"%s\" is not supported!"
5616 "\n", argv[0], argv[i]);
5617 ERROR("%s: Compressors available:\n", argv[0]);
5618 display_compressors("", COMP_DEFAULT);
5619 exit(1);
5620 }
5621 if(prev_comp != NULL && prev_comp != comp) {
5622 ERROR("%s: -comp multiple conflicting -comp"
5623 " options specified on command line"
5624 ", previously %s, now %s\n", argv[0],
5625 prev_comp->name, comp->name);
5626 exit(1);
5627 }
5628 compressor_opt_parsed = 1;
5629
5630 } else if(strcmp(argv[i], "-e") == 0)
5631 break;
5632 else if(strcmp(argv[i], "-root-becomes") == 0 ||
5633 strcmp(argv[i], "-ef") == 0 ||
5634 strcmp(argv[i], "-pf") == 0 ||
5635 strcmp(argv[i], "-vaf") == 0 ||
5636 strcmp(argv[i], "-comp") == 0)
5637 i++;
5638 }
5639
5640 /*
5641 * if no -comp option specified lookup default compressor. Note the
5642 * Makefile ensures the default compressor has been built, and so we
5643 * don't need to to check for failure here
5644 */
5645 if(comp == NULL)
5646 comp = lookup_compressor(COMP_DEFAULT);
5647
5648 for(i = source + 2; i < argc; i++) {
5649 if(strcmp(argv[i], "-action") == 0 ||
5650 strcmp(argv[i], "-a") ==0) {
5651 if(++i == argc) {
5652 ERROR("%s: %s missing action\n",
5653 argv[0], argv[i - 1]);
5654 exit(1);
5655 }
5656 res = parse_action(argv[i], ACTION_LOG_NONE);
5657 if(res == 0)
5658 exit(1);
5659
5660 } else if(strcmp(argv[i], "-verbose-action") == 0 ||
5661 strcmp(argv[i], "-va") ==0) {
5662 if(++i == argc) {
5663 ERROR("%s: %s missing action\n",
5664 argv[0], argv[i - 1]);
5665 exit(1);
5666 }
5667 res = parse_action(argv[i], ACTION_LOG_VERBOSE);
5668 if(res == 0)
5669 exit(1);
5670
5671 } else if(strcmp(argv[i], "-true-action") == 0 ||
5672 strcmp(argv[i], "-ta") ==0) {
5673 if(++i == argc) {
5674 ERROR("%s: %s missing action\n",
5675 argv[0], argv[i - 1]);
5676 exit(1);
5677 }
5678 res = parse_action(argv[i], ACTION_LOG_TRUE);
5679 if(res == 0)
5680 exit(1);
5681
5682 } else if(strcmp(argv[i], "-false-action") == 0 ||
5683 strcmp(argv[i], "-fa") ==0) {
5684 if(++i == argc) {
5685 ERROR("%s: %s missing action\n",
5686 argv[0], argv[i - 1]);
5687 exit(1);
5688 }
5689 res = parse_action(argv[i], ACTION_LOG_FALSE);
5690 if(res == 0)
5691 exit(1);
5692
5693 } else if(strcmp(argv[i], "-action-file") == 0 ||
5694 strcmp(argv[i], "-af") ==0) {
5695 if(++i == argc) {
5696 ERROR("%s: %s missing filename\n", argv[0],
5697 argv[i - 1]);
5698 exit(1);
5699 }
5700 if(read_action_file(argv[i], ACTION_LOG_NONE) == FALSE)
5701 exit(1);
5702
5703 } else if(strcmp(argv[i], "-verbose-action-file") == 0 ||
5704 strcmp(argv[i], "-vaf") ==0) {
5705 if(++i == argc) {
5706 ERROR("%s: %s missing filename\n", argv[0],
5707 argv[i - 1]);
5708 exit(1);
5709 }
5710 if(read_action_file(argv[i], ACTION_LOG_VERBOSE) == FALSE)
5711 exit(1);
5712
5713 } else if(strcmp(argv[i], "-true-action-file") == 0 ||
5714 strcmp(argv[i], "-taf") ==0) {
5715 if(++i == argc) {
5716 ERROR("%s: %s missing filename\n", argv[0],
5717 argv[i - 1]);
5718 exit(1);
5719 }
5720 if(read_action_file(argv[i], ACTION_LOG_TRUE) == FALSE)
5721 exit(1);
5722
5723 } else if(strcmp(argv[i], "-false-action-file") == 0 ||
5724 strcmp(argv[i], "-faf") ==0) {
5725 if(++i == argc) {
5726 ERROR("%s: %s missing filename\n", argv[0],
5727 argv[i - 1]);
5728 exit(1);
5729 }
5730 if(read_action_file(argv[i], ACTION_LOG_FALSE) == FALSE)
5731 exit(1);
5732
5733 } else if(strcmp(argv[i], "-comp") == 0)
5734 /* parsed previously */
5735 i++;
5736
5737 else if(strncmp(argv[i], "-X", 2) == 0) {
5738 int args;
5739
5740 if(strcmp(argv[i] + 2, "help") == 0)
5741 goto print_compressor_options;
5742
5743 args = compressor_options(comp, argv + i, argc - i);
5744 if(args < 0) {
5745 if(args == -1) {
5746 ERROR("%s: Unrecognised compressor"
5747 " option %s\n", argv[0],
5748 argv[i]);
5749 if(!compressor_opt_parsed)
5750 ERROR("%s: Did you forget to"
5751 " specify -comp?\n",
5752 argv[0]);
5753 print_compressor_options:
5754 ERROR("%s: selected compressor \"%s\""
5755 ". Options supported: %s\n",
5756 argv[0], comp->name,
5757 comp->usage ? "" : "none");
5758 if(comp->usage)
5759 comp->usage();
5760 }
5761 exit(1);
5762 }
5763 i += args;
5764
5765 } else if(strcmp(argv[i], "-pf") == 0) {
5766 if(++i == argc) {
5767 ERROR("%s: -pf missing filename\n", argv[0]);
5768 exit(1);
5769 }
5770 if(read_pseudo_file(argv[i]) == FALSE)
5771 exit(1);
5772 } else if(strcmp(argv[i], "-p") == 0) {
5773 if(++i == argc) {
5774 ERROR("%s: -p missing pseudo file definition\n",
5775 argv[0]);
5776 exit(1);
5777 }
5778 if(read_pseudo_def(argv[i]) == FALSE)
5779 exit(1);
5780 } else if(strcmp(argv[i], "-recover") == 0) {
5781 if(++i == argc) {
5782 ERROR("%s: -recover missing recovery file\n",
5783 argv[0]);
5784 exit(1);
5785 }
5786 read_recovery_data(argv[i], argv[source + 1]);
5787 } else if(strcmp(argv[i], "-no-recovery") == 0)
5788 recover = FALSE;
5789 else if(strcmp(argv[i], "-wildcards") == 0) {
5790 old_exclude = FALSE;
5791 use_regex = FALSE;
5792 } else if(strcmp(argv[i], "-regex") == 0) {
5793 old_exclude = FALSE;
5794 use_regex = TRUE;
5795 } else if(strcmp(argv[i], "-no-sparse") == 0)
5796 sparse_files = FALSE;
5797 else if(strcmp(argv[i], "-no-progress") == 0)
5798 progress = FALSE;
5799 else if(strcmp(argv[i], "-progress") == 0)
5800 force_progress = TRUE;
5801 else if(strcmp(argv[i], "-no-exports") == 0)
5802 exportable = FALSE;
5803 else if(strcmp(argv[i], "-processors") == 0) {
5804 if((++i == argc) || !parse_num(argv[i], &processors)) {
5805 ERROR("%s: -processors missing or invalid "
5806 "processor number\n", argv[0]);
5807 exit(1);
5808 }
5809 if(processors < 1) {
5810 ERROR("%s: -processors should be 1 or larger\n",
5811 argv[0]);
5812 exit(1);
5813 }
5814 } else if(strcmp(argv[i], "-read-queue") == 0) {
5815 if((++i == argc) || !parse_num(argv[i], &readq)) {
5816 ERROR("%s: -read-queue missing or invalid "
5817 "queue size\n", argv[0]);
5818 exit(1);
5819 }
5820 if(readq < 1) {
5821 ERROR("%s: -read-queue should be 1 megabyte or "
5822 "larger\n", argv[0]);
5823 exit(1);
5824 }
5825 } else if(strcmp(argv[i], "-write-queue") == 0) {
5826 if((++i == argc) || !parse_num(argv[i], &bwriteq)) {
5827 ERROR("%s: -write-queue missing or invalid "
5828 "queue size\n", argv[0]);
5829 exit(1);
5830 }
5831 if(bwriteq < 2) {
5832 ERROR("%s: -write-queue should be 2 megabytes "
5833 "or larger\n", argv[0]);
5834 exit(1);
5835 }
5836 fwriteq = bwriteq >> 1;
5837 bwriteq -= fwriteq;
5838 } else if(strcmp(argv[i], "-fragment-queue") == 0) {
5839 if((++i == argc) || !parse_num(argv[i], &fragq)) {
5840 ERROR("%s: -fragment-queue missing or invalid "
5841 "queue size\n", argv[0]);
5842 exit(1);
5843 }
5844 if(fragq < 1) {
5845 ERROR("%s: -fragment-queue should be 1 "
5846 "megabyte or larger\n", argv[0]);
5847 exit(1);
5848 }
5849 } else if(strcmp(argv[i], "-mem") == 0) {
5850 long long number;
5851
5852 if((++i == argc) ||
5853 !parse_numberll(argv[i], &number, 1)) {
5854 ERROR("%s: -mem missing or invalid mem size\n",
5855 argv[0]);
5856 exit(1);
5857 }
5858
5859 /*
5860 * convert from bytes to Mbytes, ensuring the value
5861 * does not overflow a signed int
5862 */
5863 if(number >= (1LL << 51)) {
5864 ERROR("%s: -mem invalid mem size\n", argv[0]);
5865 exit(1);
5866 }
5867
5868 total_mem = number / 1048576;
5869 if(total_mem < (SQUASHFS_LOWMEM / SQUASHFS_TAKE)) {
5870 ERROR("%s: -mem should be %d Mbytes or "
5871 "larger\n", argv[0],
5872 SQUASHFS_LOWMEM / SQUASHFS_TAKE);
5873 exit(1);
5874 }
5875 calculate_queue_sizes(total_mem, &readq, &fragq,
5876 &bwriteq, &fwriteq);
5877 } else if(strcmp(argv[i], "-b") == 0) {
5878 if(++i == argc) {
5879 ERROR("%s: -b missing block size\n", argv[0]);
5880 exit(1);
5881 }
5882 if(!parse_number(argv[i], &block_size, 1)) {
5883 ERROR("%s: -b invalid block size\n", argv[0]);
5884 exit(1);
5885 }
5886 if((block_log = slog(block_size)) == 0) {
5887 ERROR("%s: -b block size not power of two or "
5888 "not between 4096 and 1Mbyte\n",
5889 argv[0]);
5890 exit(1);
5891 }
5892 } else if(strcmp(argv[i], "-ef") == 0) {
5893 if(++i == argc) {
5894 ERROR("%s: -ef missing filename\n", argv[0]);
5895 exit(1);
5896 }
5897 } else if(strcmp(argv[i], "-no-duplicates") == 0)
5898 duplicate_checking = FALSE;
5899
5900 else if(strcmp(argv[i], "-no-fragments") == 0)
5901 no_fragments = TRUE;
5902
5903 else if(strcmp(argv[i], "-always-use-fragments") == 0)
5904 always_use_fragments = TRUE;
5905
5906 else if(strcmp(argv[i], "-sort") == 0) {
5907 if(++i == argc) {
5908 ERROR("%s: -sort missing filename\n", argv[0]);
5909 exit(1);
5910 }
5911 } else if(strcmp(argv[i], "-all-root") == 0 ||
5912 strcmp(argv[i], "-root-owned") == 0)
5913 global_uid = global_gid = 0;
5914
5915 else if(strcmp(argv[i], "-force-uid") == 0) {
5916 if(++i == argc) {
5917 ERROR("%s: -force-uid missing uid or user\n",
5918 argv[0]);
5919 exit(1);
5920 }
5921 if((global_uid = strtoll(argv[i], &b, 10)), *b =='\0') {
5922 if(global_uid < 0 || global_uid >
5923 (((long long) 1 << 32) - 1)) {
5924 ERROR("%s: -force-uid uid out of range"
5925 "\n", argv[0]);
5926 exit(1);
5927 }
5928 } else {
5929 struct passwd *uid = getpwnam(argv[i]);
5930 if(uid)
5931 global_uid = uid->pw_uid;
5932 else {
5933 ERROR("%s: -force-uid invalid uid or "
5934 "unknown user\n", argv[0]);
5935 exit(1);
5936 }
5937 }
5938 } else if(strcmp(argv[i], "-force-gid") == 0) {
5939 if(++i == argc) {
5940 ERROR("%s: -force-gid missing gid or group\n",
5941 argv[0]);
5942 exit(1);
5943 }
5944 if((global_gid = strtoll(argv[i], &b, 10)), *b =='\0') {
5945 if(global_gid < 0 || global_gid >
5946 (((long long) 1 << 32) - 1)) {
5947 ERROR("%s: -force-gid gid out of range"
5948 "\n", argv[0]);
5949 exit(1);
5950 }
5951 } else {
5952 struct group *gid = getgrnam(argv[i]);
5953 if(gid)
5954 global_gid = gid->gr_gid;
5955 else {
5956 ERROR("%s: -force-gid invalid gid or "
5957 "unknown group\n", argv[0]);
5958 exit(1);
5959 }
5960 }
5961 } else if(strcmp(argv[i], "-noI") == 0 ||
5962 strcmp(argv[i], "-noInodeCompression") == 0)
5963 noI = TRUE;
5964
5965 else if(strcmp(argv[i], "-noD") == 0 ||
5966 strcmp(argv[i], "-noDataCompression") == 0)
5967 noD = TRUE;
5968
5969 else if(strcmp(argv[i], "-noF") == 0 ||
5970 strcmp(argv[i], "-noFragmentCompression") == 0)
5971 noF = TRUE;
5972
5973 else if(strcmp(argv[i], "-noX") == 0 ||
5974 strcmp(argv[i], "-noXattrCompression") == 0)
5975 noX = TRUE;
5976
5977 else if(strcmp(argv[i], "-no-xattrs") == 0)
5978 no_xattrs = TRUE;
5979
5980 else if(strcmp(argv[i], "-xattrs") == 0)
5981 no_xattrs = FALSE;
5982
5983 /* ANDROID CHANGES START*/
5984 #ifdef ANDROID
5985 else if(strcmp(argv[i], "-context-file") == 0) {
5986 if(++i == argc) {
5987 ERROR("%s: -context-file: missing file name\n",
5988 argv[0]);
5989 exit(1);
5990 }
5991 context_file = argv[i];
5992 }
5993 else if(strcmp(argv[i], "-fs-config-file") == 0) {
5994 if(++i == argc) {
5995 ERROR("%s: -fs-config-file: missing file name\n",
5996 argv[0]);
5997 exit(1);
5998 }
5999 fs_config_file = argv[i];
6000 } else if(strcmp(argv[i], "-whitelist") == 0) {
6001 if(++i == argc) {
6002 ERROR("%s: -whitelist missing filename\n", argv[0]);
6003 exit(1);
6004 }
6005 whitelist_filename = argv[i];
6006 }
6007 else if(strcmp(argv[i], "-t") == 0) {
6008 if(++i == argc) {
6009 ERROR("%s: -t missing compression threshold percentage\n", argv[0]);
6010 exit(1);
6011 }
6012 if(!parse_number(argv[i], &compress_thresh_per, 1)) {
6013 ERROR("%s: -t invalid compression threshold percentage\n", argv[0]);
6014 exit(1);
6015 }
6016 if(compress_thresh_per > 100 || compress_thresh_per < 0) {
6017 ERROR("%s: -t compression threshold percentage not between 0 and 100\n",
6018 argv[0]);
6019 exit(1);
6020 }
6021 }
6022 #endif
6023 /* ANDROID CHANGES END */
6024 else if(strcmp(argv[i], "-nopad") == 0)
6025 nopad = TRUE;
6026
6027 else if(strcmp(argv[i], "-info") == 0)
6028 silent = FALSE;
6029
6030 else if(strcmp(argv[i], "-e") == 0)
6031 break;
6032
6033 else if(strcmp(argv[i], "-noappend") == 0)
6034 delete = TRUE;
6035
6036 else if(strcmp(argv[i], "-keep-as-directory") == 0)
6037 keep_as_directory = TRUE;
6038 /* ANDROID CHANGES START*/
6039 #ifdef ANDROID
6040 else if(strcmp(argv[i], "-android-fs-config") == 0)
6041 android_config = TRUE;
6042 else if(strcmp(argv[i], "-mount-point") == 0) {
6043 if(++i == argc) {
6044 ERROR("%s: -mount-point: missing mount point name\n",
6045 argv[0]);
6046 exit(1);
6047 }
6048 mount_point = argv[i];
6049 }
6050 else if(strcmp(argv[i], "-product-out") == 0) {
6051 if(++i == argc) {
6052 ERROR("%s: -product-out: missing path name\n",
6053 argv[0]);
6054 exit(1);
6055 }
6056 target_out_path = argv[i];
6057 }
6058 else if(strcmp(argv[i], "-disable-4k-align") == 0)
6059 align_4k_blocks = FALSE;
6060 else if(strcmp(argv[i], "-block-map") == 0) {
6061 if(++i == argc) {
6062 ERROR("%s: -block-map: missing path name\n",
6063 argv[0]);
6064 exit(1);
6065 }
6066 block_map_file = fopen(argv[i], "w");
6067 if (block_map_file == NULL) {
6068 ERROR("%s: -block-map: failed to open %s\n",
6069 argv[0], argv[i]);
6070 exit(1);
6071 }
6072 if (!align_4k_blocks) {
6073 ERROR("WARNING: Using block maps with unaligned 4k blocks "
6074 "is not ideal as block map offsets are multiples of 4k, "
6075 "consider not passing -disable-4k-align\n");
6076 }
6077 }
6078 #endif
6079 /* ANDROID CHANGES END */
6080
6081 else if(strcmp(argv[i], "-exit-on-error") == 0)
6082 exit_on_error = TRUE;
6083
6084 else if(strcmp(argv[i], "-root-becomes") == 0) {
6085 if(++i == argc) {
6086 ERROR("%s: -root-becomes: missing name\n",
6087 argv[0]);
6088 exit(1);
6089 }
6090 root_name = argv[i];
6091 } else if (strcmp(argv[i], "-uid-map") == 0) {
6092 if (++i == argc) {
6093 ERROR("%s: -uid-map: missing mapping\n",
6094 argv[0]);
6095 exit(1);
6096 }
6097 if (parse_ugid_map(argv[i], uid_mapping,
6098 &uid_map_count) != 0) {
6099 ERROR("%s: -uid-map: invalid mapping\n",
6100 argv[0]);
6101 exit(1);
6102 }
6103 } else if (strcmp(argv[i], "-gid-map") == 0) {
6104 if (++i == argc) {
6105 ERROR("%s: -gid-map: missing mapping\n",
6106 argv[0]);
6107 exit(1);
6108 }
6109 if (parse_ugid_map(argv[i], gid_mapping,
6110 &gid_map_count) != 0) {
6111 ERROR("%s: -gid-map: invalid mapping\n",
6112 argv[0]);
6113 exit(1);
6114 }
6115 } else if(strcmp(argv[i], "-version") == 0) {
6116 VERSION();
6117 } else {
6118 ERROR("%s: invalid option\n\n", argv[0]);
6119 printOptions:
6120 ERROR("SYNTAX:%s source1 source2 ... dest [options] "
6121 "[-e list of exclude\ndirs/files]\n", argv[0]);
6122 ERROR("\nFilesystem build options:\n");
6123 ERROR("-comp <comp>\t\tselect <comp> compression\n");
6124 ERROR("\t\t\tCompressors available:\n");
6125 display_compressors("\t\t\t", COMP_DEFAULT);
6126 ERROR("-b <block_size>\t\tset data block to "
6127 "<block_size>. Default 128 Kbytes\n");
6128 ERROR("\t\t\tOptionally a suffix of K or M can be"
6129 " given to specify\n\t\t\tKbytes or Mbytes"
6130 " respectively\n");
6131 ERROR("-no-exports\t\tdon't make the filesystem "
6132 "exportable via NFS\n");
6133 ERROR("-no-sparse\t\tdon't detect sparse files\n");
6134 ERROR("-no-xattrs\t\tdon't store extended attributes"
6135 NOXOPT_STR "\n");
6136 ERROR("-xattrs\t\t\tstore extended attributes" XOPT_STR
6137 "\n");
6138 /* ANDROID CHANGES START*/
6139 #ifdef ANDROID
6140 ERROR("-context-file <file>\tApply selinux security "
6141 "xattrs from context-file instead\n\t\t\t"
6142 "of reading xattrs from file system\n");
6143 ERROR("-fs-config-file <file>\tAndroid specific "
6144 "filesystem config file\n");
6145 ERROR("-t <compress_thresh>\tset minimum "
6146 "acceptable compression ratio of a block to\n\t\t\t"
6147 "<compress_thresh_per> otherwise don't compress. "
6148 "Default 0%\n");
6149 ERROR("-whitelist <file>\tAndroid specific whitelist "
6150 "one entry per line (no wildcards)\n");
6151 #endif
6152 /* ANDROID CHANGES END */
6153 ERROR("-noI\t\t\tdo not compress inode table\n");
6154 ERROR("-noD\t\t\tdo not compress data blocks\n");
6155 ERROR("-noF\t\t\tdo not compress fragment blocks\n");
6156 ERROR("-noX\t\t\tdo not compress extended "
6157 "attributes\n");
6158 ERROR("-no-fragments\t\tdo not use fragments\n");
6159 ERROR("-always-use-fragments\tuse fragment blocks for "
6160 "files larger than block size\n");
6161 ERROR("-no-duplicates\t\tdo not perform duplicate "
6162 "checking\n");
6163 ERROR("-all-root\t\tmake all files owned by root\n");
6164 ERROR("-force-uid uid\t\tset all file uids to uid\n");
6165 ERROR("-force-gid gid\t\tset all file gids to gid\n");
6166 ERROR("-nopad\t\t\tdo not pad filesystem to a multiple "
6167 "of 4K\n");
6168 ERROR("-keep-as-directory\tif one source directory is "
6169 "specified, create a root\n");
6170 ERROR("\t\t\tdirectory containing that directory, "
6171 "rather than the\n");
6172 ERROR("\t\t\tcontents of the directory\n");
6173 /* ANDROID CHANGES START*/
6174 #ifdef ANDROID
6175 ERROR("-android-fs-config\tuse android fs config "
6176 "for mode, uid, and gids of inodes\n");
6177 ERROR("-mount-point <name>\tNeed to be provided when "
6178 "android-fs-config or context-file\n\t\t\tare "
6179 "enabled and source directory is not mount point\n");
6180 ERROR("-product-out <path>\tPRODUCT_OUT directory to "
6181 "read device specific FS rules files from\n");
6182 ERROR("-disable-4k-align \tDon't 4k align data blocks. Default is false\n");
6183 ERROR("-block-map <path>\tGenerate a block map for non-fragment files\n");
6184 #endif
6185 /* ANDROID CHANGES END */
6186 ERROR("\nFilesystem filter options:\n");
6187 ERROR("-p <pseudo-definition>\tAdd pseudo file "
6188 "definition\n");
6189 ERROR("-pf <pseudo-file>\tAdd list of pseudo file "
6190 "definitions\n");
6191 ERROR("-sort <sort_file>\tsort files according to "
6192 "priorities in <sort_file>. One\n");
6193 ERROR("\t\t\tfile or dir with priority per line. "
6194 "Priority -32768 to\n");
6195 ERROR("\t\t\t32767, default priority 0\n");
6196 ERROR("-ef <exclude_file>\tlist of exclude dirs/files."
6197 " One per line\n");
6198 ERROR("-wildcards\t\tAllow extended shell wildcards "
6199 "(globbing) to be used in\n\t\t\texclude "
6200 "dirs/files\n");
6201 ERROR("-regex\t\t\tAllow POSIX regular expressions to "
6202 "be used in exclude\n\t\t\tdirs/files\n");
6203 ERROR("-uid-map <mapping>\tUser ID mapping.\n");
6204 ERROR("\t\t\tFollows the format described in "
6205 "user_namespaces(7).\n");
6206 ERROR("-gid-map <mapping>\tGroup ID mapping.\n");
6207 ERROR("\t\t\tFollows the format described in "
6208 "user_namespaces(7).\n");
6209 ERROR("\nFilesystem append options:\n");
6210 ERROR("-noappend\t\tdo not append to existing "
6211 "filesystem\n");
6212 ERROR("-root-becomes <name>\twhen appending source "
6213 "files/directories, make the\n");
6214 ERROR("\t\t\toriginal root become a subdirectory in "
6215 "the new root\n");
6216 ERROR("\t\t\tcalled <name>, rather than adding the new "
6217 "source items\n");
6218 ERROR("\t\t\tto the original root\n");
6219 ERROR("\nMksquashfs runtime options:\n");
6220 ERROR("-version\t\tprint version, licence and "
6221 "copyright message\n");
6222 ERROR("-exit-on-error\t\ttreat normally ignored errors "
6223 "as fatal\n");
6224 ERROR("-recover <name>\t\trecover filesystem data "
6225 "using recovery file <name>\n");
6226 ERROR("-no-recovery\t\tdon't generate a recovery "
6227 "file\n");
6228 ERROR("-info\t\t\tprint files written to filesystem\n");
6229 ERROR("-no-progress\t\tdon't display the progress "
6230 "bar\n");
6231 ERROR("-progress\t\tdisplay progress bar when using "
6232 "the -info option\n");
6233 ERROR("-processors <number>\tUse <number> processors."
6234 " By default will use number of\n");
6235 ERROR("\t\t\tprocessors available\n");
6236 ERROR("-mem <size>\t\tUse <size> physical memory. "
6237 "Currently set to %dM\n", total_mem);
6238 ERROR("\t\t\tOptionally a suffix of K, M or G can be"
6239 " given to specify\n\t\t\tKbytes, Mbytes or"
6240 " Gbytes respectively\n");
6241 ERROR("\nMiscellaneous options:\n");
6242 ERROR("-root-owned\t\talternative name for -all-root"
6243 "\n");
6244 ERROR("-noInodeCompression\talternative name for -noI"
6245 "\n");
6246 ERROR("-noDataCompression\talternative name for -noD"
6247 "\n");
6248 ERROR("-noFragmentCompression\talternative name for "
6249 "-noF\n");
6250 ERROR("-noXattrCompression\talternative name for "
6251 "-noX\n");
6252 ERROR("\n-Xhelp\t\t\tprint compressor options for"
6253 " selected compressor\n");
6254 ERROR("\nCompressors available and compressor specific "
6255 "options:\n");
6256 display_compressor_usage(COMP_DEFAULT);
6257 exit(1);
6258 }
6259 }
6260
6261 if (!uid_map_count) {
6262 uid_mapping[0].child_id = 0;
6263 uid_mapping[0].parent_id = 0;
6264 uid_mapping[0].length = 4294967295u;
6265 uid_map_count = 1;
6266 }
6267 if (!gid_map_count) {
6268 gid_mapping[0].child_id = 0;
6269 gid_mapping[0].parent_id = 0;
6270 gid_mapping[0].length = 4294967295u;
6271 gid_map_count = 1;
6272 }
6273
6274 /* ANDROID CHANGES START*/
6275 #ifdef ANDROID
6276 if (fs_config_file) {
6277 if (load_canned_fs_config(fs_config_file) < 0) {
6278 fprintf(stderr, "failed to load %s\n", fs_config_file);
6279 exit(1);
6280 }
6281 fs_config_func = canned_fs_config;
6282 } else if (mount_point) {
6283 fs_config_func = fs_config;
6284 }
6285 if (whitelist_filename)
6286 process_whitelist_file(whitelist_filename);
6287 #endif
6288 /* ANDROID CHANGES END */
6289
6290 /*
6291 * Some compressors may need the options to be checked for validity
6292 * once all the options have been processed
6293 */
6294 res = compressor_options_post(comp, block_size);
6295 if(res)
6296 EXIT_MKSQUASHFS();
6297
6298 /*
6299 * If the -info option has been selected then disable the
6300 * progress bar unless it has been explicitly enabled with
6301 * the -progress option
6302 */
6303 if(!silent)
6304 progress = force_progress;
6305
6306 #ifdef SQUASHFS_TRACE
6307 /*
6308 * Disable progress bar if full debug tracing is enabled.
6309 * The progress bar in this case just gets in the way of the
6310 * debug trace output
6311 */
6312 progress = FALSE;
6313 #endif
6314
6315 for(i = 0; i < source; i++)
6316 if(lstat(source_path[i], &source_buf) == -1) {
6317 fprintf(stderr, "Cannot stat source directory \"%s\" "
6318 "because %s\n", source_path[i],
6319 strerror(errno));
6320 EXIT_MKSQUASHFS();
6321 }
6322
6323 destination_file = argv[source + 1];
6324 if(stat(argv[source + 1], &buf) == -1) {
6325 if(errno == ENOENT) { /* Does not exist */
6326 fd = open(argv[source + 1], O_CREAT | O_TRUNC | O_RDWR,
6327 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
6328 if(fd == -1) {
6329 perror("Could not create destination file");
6330 exit(1);
6331 }
6332 delete = TRUE;
6333 } else {
6334 perror("Could not stat destination file");
6335 exit(1);
6336 }
6337
6338 } else {
6339 if(S_ISBLK(buf.st_mode)) {
6340 if((fd = open(argv[source + 1], O_RDWR)) == -1) {
6341 perror("Could not open block device as "
6342 "destination");
6343 exit(1);
6344 }
6345 block_device = 1;
6346
6347 } else if(S_ISREG(buf.st_mode)) {
6348 fd = open(argv[source + 1], (delete ? O_TRUNC : 0) |
6349 O_RDWR);
6350 if(fd == -1) {
6351 perror("Could not open regular file for "
6352 "writing as destination");
6353 exit(1);
6354 }
6355 }
6356 else {
6357 ERROR("Destination not block device or regular file\n");
6358 exit(1);
6359 }
6360
6361 }
6362
6363 /*
6364 * process the exclude files - must be done afer destination file has
6365 * been possibly created
6366 */
6367 for(i = source + 2; i < argc; i++)
6368 if(strcmp(argv[i], "-ef") == 0)
6369 /*
6370 * Note presence of filename arg has already
6371 * been checked
6372 */
6373 process_exclude_file(argv[++i]);
6374 else if(strcmp(argv[i], "-e") == 0)
6375 break;
6376 else if(strcmp(argv[i], "-root-becomes") == 0 ||
6377 strcmp(argv[i], "-sort") == 0 ||
6378 strcmp(argv[i], "-pf") == 0 ||
6379 strcmp(argv[i], "-af") == 0 ||
6380 strcmp(argv[i], "-vaf") == 0 ||
6381 strcmp(argv[i], "-comp") == 0)
6382 i++;
6383
6384 if(i != argc) {
6385 if(++i == argc) {
6386 ERROR("%s: -e missing arguments\n", argv[0]);
6387 EXIT_MKSQUASHFS();
6388 }
6389 while(i < argc)
6390 if(old_exclude)
6391 old_add_exclude(argv[i++]);
6392 else
6393 add_exclude(argv[i++]);
6394 }
6395
6396 /* process the sort files - must be done afer the exclude files */
6397 for(i = source + 2; i < argc; i++)
6398 if(strcmp(argv[i], "-sort") == 0) {
6399 int res = read_sort_file(argv[++i], source,
6400 source_path);
6401 if(res == FALSE)
6402 BAD_ERROR("Failed to read sort file\n");
6403 sorted ++;
6404 } else if(strcmp(argv[i], "-e") == 0)
6405 break;
6406 else if(strcmp(argv[i], "-root-becomes") == 0 ||
6407 strcmp(argv[i], "-ef") == 0 ||
6408 strcmp(argv[i], "-pf") == 0 ||
6409 strcmp(argv[i], "-af") == 0 ||
6410 strcmp(argv[i], "-vaf") == 0 ||
6411 strcmp(argv[i], "-comp") == 0)
6412 i++;
6413
6414 if(!delete) {
6415 comp = read_super(fd, &sBlk, argv[source + 1]);
6416 if(comp == NULL) {
6417 ERROR("Failed to read existing filesystem - will not "
6418 "overwrite - ABORTING!\n");
6419 ERROR("To force Mksquashfs to write to this block "
6420 "device or file use -noappend\n");
6421 EXIT_MKSQUASHFS();
6422 }
6423
6424 block_log = slog(block_size = sBlk.block_size);
6425 noI = SQUASHFS_UNCOMPRESSED_INODES(sBlk.flags);
6426 noD = SQUASHFS_UNCOMPRESSED_DATA(sBlk.flags);
6427 noF = SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.flags);
6428 noX = SQUASHFS_UNCOMPRESSED_XATTRS(sBlk.flags);
6429 no_fragments = SQUASHFS_NO_FRAGMENTS(sBlk.flags);
6430 always_use_fragments = SQUASHFS_ALWAYS_FRAGMENTS(sBlk.flags);
6431 duplicate_checking = SQUASHFS_DUPLICATES(sBlk.flags);
6432 exportable = SQUASHFS_EXPORTABLE(sBlk.flags);
6433 no_xattrs = SQUASHFS_NO_XATTRS(sBlk.flags);
6434 comp_opts = SQUASHFS_COMP_OPTS(sBlk.flags);
6435 }
6436
6437 initialise_threads(readq, fragq, bwriteq, fwriteq, delete,
6438 destination_file);
6439
6440 res = compressor_init(comp, &stream, SQUASHFS_METADATA_SIZE, 0);
6441 if(res)
6442 BAD_ERROR("compressor_init failed\n");
6443
6444 if(delete) {
6445 int size;
6446 void *comp_data = compressor_dump_options(comp, block_size,
6447 &size);
6448
6449 printf("Creating %d.%d filesystem on %s, block size %d.\n",
6450 SQUASHFS_MAJOR, SQUASHFS_MINOR, argv[source + 1], block_size);
6451
6452 /*
6453 * store any compressor specific options after the superblock,
6454 * and set the COMP_OPT flag to show that the filesystem has
6455 * compressor specfic options
6456 */
6457 if(comp_data) {
6458 unsigned short c_byte = size | SQUASHFS_COMPRESSED_BIT;
6459
6460 SQUASHFS_INSWAP_SHORTS(&c_byte, 1);
6461 write_destination(fd, sizeof(struct squashfs_super_block),
6462 sizeof(c_byte), &c_byte);
6463 write_destination(fd, sizeof(struct squashfs_super_block) +
6464 sizeof(c_byte), size, comp_data);
6465 bytes = sizeof(struct squashfs_super_block) + sizeof(c_byte)
6466 + size;
6467 comp_opts = TRUE;
6468 } else
6469 bytes = sizeof(struct squashfs_super_block);
6470 } else {
6471 unsigned int last_directory_block, inode_dir_offset,
6472 inode_dir_file_size, root_inode_size,
6473 inode_dir_start_block, uncompressed_data,
6474 compressed_data, inode_dir_inode_number,
6475 inode_dir_parent_inode;
6476 unsigned int root_inode_start =
6477 SQUASHFS_INODE_BLK(sBlk.root_inode),
6478 root_inode_offset =
6479 SQUASHFS_INODE_OFFSET(sBlk.root_inode);
6480
6481 if((bytes = read_filesystem(root_name, fd, &sBlk, &inode_table,
6482 &data_cache, &directory_table,
6483 &directory_data_cache, &last_directory_block,
6484 &inode_dir_offset, &inode_dir_file_size,
6485 &root_inode_size, &inode_dir_start_block,
6486 &file_count, &sym_count, &dev_count, &dir_count,
6487 &fifo_count, &sock_count, &total_bytes,
6488 &total_inode_bytes, &total_directory_bytes,
6489 &inode_dir_inode_number,
6490 &inode_dir_parent_inode, add_old_root_entry,
6491 &fragment_table, &inode_lookup_table)) == 0) {
6492 ERROR("Failed to read existing filesystem - will not "
6493 "overwrite - ABORTING!\n");
6494 ERROR("To force Mksquashfs to write to this block "
6495 "device or file use -noappend\n");
6496 EXIT_MKSQUASHFS();
6497 }
6498 if((append_fragments = fragments = sBlk.fragments)) {
6499 fragment_table = realloc((char *) fragment_table,
6500 ((fragments + FRAG_SIZE - 1) & ~(FRAG_SIZE - 1))
6501 * sizeof(struct squashfs_fragment_entry));
6502 if(fragment_table == NULL)
6503 BAD_ERROR("Out of memory in save filesystem state\n");
6504 }
6505
6506 printf("Appending to existing %d.%d filesystem on %s, block "
6507 "size %d\n", SQUASHFS_MAJOR, SQUASHFS_MINOR, argv[source + 1],
6508 block_size);
6509 printf("All -b, -noI, -noD, -noF, -noX, no-duplicates, no-fragments, "
6510 "-always-use-fragments,\n-exportable and -comp options "
6511 "ignored\n");
6512 printf("\nIf appending is not wanted, please re-run with "
6513 "-noappend specified!\n\n");
6514
6515 compressed_data = (inode_dir_offset + inode_dir_file_size) &
6516 ~(SQUASHFS_METADATA_SIZE - 1);
6517 uncompressed_data = (inode_dir_offset + inode_dir_file_size) &
6518 (SQUASHFS_METADATA_SIZE - 1);
6519
6520 /* save original filesystem state for restoring ... */
6521 sfragments = fragments;
6522 sbytes = bytes;
6523 sinode_count = sBlk.inodes;
6524 scache_bytes = root_inode_offset + root_inode_size;
6525 sdirectory_cache_bytes = uncompressed_data;
6526 sdata_cache = malloc(scache_bytes);
6527 if(sdata_cache == NULL)
6528 BAD_ERROR("Out of memory in save filesystem state\n");
6529 sdirectory_data_cache = malloc(sdirectory_cache_bytes);
6530 if(sdirectory_data_cache == NULL)
6531 BAD_ERROR("Out of memory in save filesystem state\n");
6532 memcpy(sdata_cache, data_cache, scache_bytes);
6533 memcpy(sdirectory_data_cache, directory_data_cache +
6534 compressed_data, sdirectory_cache_bytes);
6535 sinode_bytes = root_inode_start;
6536 stotal_bytes = total_bytes;
6537 stotal_inode_bytes = total_inode_bytes;
6538 stotal_directory_bytes = total_directory_bytes +
6539 compressed_data;
6540 sfile_count = file_count;
6541 ssym_count = sym_count;
6542 sdev_count = dev_count;
6543 sdir_count = dir_count + 1;
6544 sfifo_count = fifo_count;
6545 ssock_count = sock_count;
6546 sdup_files = dup_files;
6547 sid_count = id_count;
6548 write_recovery_data(&sBlk);
6549 save_xattrs();
6550 appending = TRUE;
6551
6552 /*
6553 * set the filesystem state up to be able to append to the
6554 * original filesystem. The filesystem state differs depending
6555 * on whether we're appending to the original root directory, or
6556 * if the original root directory becomes a sub-directory
6557 * (root-becomes specified on command line, here root_name !=
6558 * NULL)
6559 */
6560 inode_bytes = inode_size = root_inode_start;
6561 directory_size = last_directory_block;
6562 cache_size = root_inode_offset + root_inode_size;
6563 directory_cache_size = inode_dir_offset + inode_dir_file_size;
6564 if(root_name) {
6565 sdirectory_bytes = last_directory_block;
6566 sdirectory_compressed_bytes = 0;
6567 root_inode_number = inode_dir_parent_inode;
6568 inode_no = sBlk.inodes + 2;
6569 directory_bytes = last_directory_block;
6570 directory_cache_bytes = uncompressed_data;
6571 memmove(directory_data_cache, directory_data_cache +
6572 compressed_data, uncompressed_data);
6573 cache_bytes = root_inode_offset + root_inode_size;
6574 add_old_root_entry(root_name, sBlk.root_inode,
6575 inode_dir_inode_number, SQUASHFS_DIR_TYPE);
6576 total_directory_bytes += compressed_data;
6577 dir_count ++;
6578 } else {
6579 sdirectory_compressed_bytes = last_directory_block -
6580 inode_dir_start_block;
6581 sdirectory_compressed =
6582 malloc(sdirectory_compressed_bytes);
6583 if(sdirectory_compressed == NULL)
6584 BAD_ERROR("Out of memory in save filesystem "
6585 "state\n");
6586 memcpy(sdirectory_compressed, directory_table +
6587 inode_dir_start_block,
6588 sdirectory_compressed_bytes);
6589 sdirectory_bytes = inode_dir_start_block;
6590 root_inode_number = inode_dir_inode_number;
6591 inode_no = sBlk.inodes + 1;
6592 directory_bytes = inode_dir_start_block;
6593 directory_cache_bytes = inode_dir_offset;
6594 cache_bytes = root_inode_offset;
6595 }
6596
6597 inode_count = file_count + dir_count + sym_count + dev_count +
6598 fifo_count + sock_count;
6599 }
6600
6601 if(path)
6602 paths = add_subdir(paths, path);
6603
6604 dump_actions();
6605 dump_pseudos();
6606
6607 if(delete && !keep_as_directory && source == 1 &&
6608 S_ISDIR(source_buf.st_mode))
6609 dir_scan(&inode, source_path[0], scan1_readdir, progress);
6610 else if(!keep_as_directory && source == 1 &&
6611 S_ISDIR(source_buf.st_mode))
6612 dir_scan(&inode, source_path[0], scan1_single_readdir, progress);
6613 else
6614 dir_scan(&inode, "", scan1_encomp_readdir, progress);
6615 sBlk.root_inode = inode;
6616 sBlk.inodes = inode_count;
6617 sBlk.s_magic = SQUASHFS_MAGIC;
6618 sBlk.s_major = SQUASHFS_MAJOR;
6619 sBlk.s_minor = SQUASHFS_MINOR;
6620 sBlk.block_size = block_size;
6621 sBlk.block_log = block_log;
6622 sBlk.flags = SQUASHFS_MKFLAGS(noI, noD, noF, noX, no_fragments,
6623 always_use_fragments, duplicate_checking, exportable,
6624 no_xattrs, comp_opts);
6625 sBlk.mkfs_time = time(NULL);
6626
6627 disable_info();
6628
6629 while((fragment = get_frag_action(fragment)))
6630 write_fragment(*fragment);
6631 unlock_fragments();
6632 pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
6633 pthread_mutex_lock(&fragment_mutex);
6634 while(fragments_outstanding) {
6635 pthread_mutex_unlock(&fragment_mutex);
6636 sched_yield();
6637 pthread_mutex_lock(&fragment_mutex);
6638 }
6639 pthread_cleanup_pop(1);
6640
6641 queue_put(to_writer, NULL);
6642 if(queue_get(from_writer) != 0)
6643 EXIT_MKSQUASHFS();
6644
6645 set_progressbar_state(FALSE);
6646 write_filesystem_tables(&sBlk, nopad);
6647
6648 /* ANDROID CHANGES START*/
6649 #ifdef ANDROID
6650 if (block_map_file)
6651 fclose(block_map_file);
6652 #endif
6653 /* ANDROID CHANGES END */
6654
6655 return 0;
6656 }
6657