1 /***
2 *
3 */
4 package com.fernsroth.squashfs;
5
6 import java.io.ByteArrayInputStream;
7 import java.io.ByteArrayOutputStream;
8 import java.io.IOException;
9 import java.math.BigInteger;
10 import java.text.DecimalFormat;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.Comparator;
14 import java.util.HashMap;
15 import java.util.Map;
16 import java.util.zip.DeflaterOutputStream;
17 import java.util.zip.InflaterInputStream;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21
22 import com.fernsroth.easyio.EasyIOInputStream;
23 import com.fernsroth.easyio.EasyIOOutputStream;
24 import com.fernsroth.easyio.IRandomAccessSource;
25 import com.fernsroth.easyio.RandomAccessByteArray;
26 import com.fernsroth.easyio.RandomAccessByteArrayInputStreamAdaptor;
27 import com.fernsroth.easyio.RandomAccessByteArrayOutputStreamAdapter;
28 import com.fernsroth.easyio.exception.EasyIOException;
29 import com.fernsroth.squashfs.exception.SquashFSException;
30 import com.fernsroth.squashfs.model.BaseFile;
31 import com.fernsroth.squashfs.model.Directory;
32 import com.fernsroth.squashfs.model.Manifest;
33 import com.fernsroth.squashfs.model.SymLink;
34 import com.fernsroth.squashfs.model.squashfs.cached_dir_index;
35 import com.fernsroth.squashfs.model.squashfs.dir_ent;
36 import com.fernsroth.squashfs.model.squashfs.dir_info;
37 import com.fernsroth.squashfs.model.squashfs.directory;
38 import com.fernsroth.squashfs.model.squashfs.duplicate_buffer_handle;
39 import com.fernsroth.squashfs.model.squashfs.file_info;
40 import com.fernsroth.squashfs.model.squashfs.fragment;
41 import com.fernsroth.squashfs.model.squashfs.inode_info;
42 import com.fernsroth.squashfs.model.squashfs.old_root_entry_info;
43 import com.fernsroth.squashfs.model.squashfs.squashfs_base_inode_header;
44 import com.fernsroth.squashfs.model.squashfs.squashfs_constants;
45 import com.fernsroth.squashfs.model.squashfs.squashfs_dir_entry;
46 import com.fernsroth.squashfs.model.squashfs.squashfs_dir_header;
47 import com.fernsroth.squashfs.model.squashfs.squashfs_dir_inode_header;
48 import com.fernsroth.squashfs.model.squashfs.squashfs_fragment_entry;
49 import com.fernsroth.squashfs.model.squashfs.squashfs_reg_inode_header;
50 import com.fernsroth.squashfs.model.squashfs.squashfs_super_block;
51 import com.fernsroth.squashfs.model.squashfs.squashfs_symlink_inode_header;
52 import com.fernsroth.squashfs.model.squashfs.stat;
53
54 /***
55 *
56 * @author Joseph M. Ferner (Near Infinity Corporation)
57 */
58 public final class SquashFSWriter {
59
60 /***
61 * logging.
62 */
63 private static Log log = LogFactory.getLog(SquashFSWriter.class);
64
65 /***
66 * the system.
67 */
68 private static SquashFSSystem system = new DefaultSquashFSSystem();
69
70 /***
71 *
72 */
73 private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat(
74 "#0.00");
75
76 /***
77 *
78 */
79 private static final int FRAG_SIZE = 32768;
80
81 /***
82 *
83 */
84 private static final fragment empty_fragment;
85
86 static {
87 empty_fragment = new fragment();
88 empty_fragment.index = (int) squashfs_constants.SQUASHFS_INVALID_FRAG;
89 }
90
91 /***
92 *
93 */
94 private Map<String, inode_info> inode_info = new HashMap<String, inode_info>();
95
96 /***
97 *
98 */
99 private squashfs_super_block superBlock;
100
101 /***
102 *
103 */
104 private long global_uid = -1;
105
106 /***
107 *
108 */
109 private long global_gid = -1;
110
111 /***
112 *
113 */
114 private byte[] directory_data_cache;
115
116 /***
117 *
118 */
119 private byte[] directory_table;
120
121 /***
122 *
123 */
124 private int directory_cache_bytes;
125
126 /***
127 *
128 */
129 private int total_bytes;
130
131 /***
132 *
133 */
134 private int uid_count;
135
136 /***
137 *
138 */
139 private int total_inode_bytes;
140
141 /***
142 *
143 */
144 private int total_directory_bytes;
145
146 /***
147 *
148 */
149 private int guid_count;
150
151 /***
152 *
153 */
154 private int inode_bytes;
155
156 /***
157 *
158 */
159 private int directory_bytes;
160
161 /***
162 *
163 */
164 private int file_count;
165
166 /***
167 *
168 */
169 private int dup_files;
170
171 /***
172 *
173 */
174 private int inode_count;
175
176 /***
177 *
178 */
179 private int dev_count;
180
181 /***
182 *
183 */
184 private int fifo_count;
185
186 /***
187 *
188 */
189 private int sock_count;
190
191 /***
192 *
193 */
194 private int dir_count;
195
196 /***
197 *
198 */
199 private int sym_count;
200
201 /***
202 *
203 */
204 private long[] uids = new long[squashfs_constants.SQUASHFS_UIDS];
205
206 /***
207 *
208 */
209 private long[] guids = new long[squashfs_constants.SQUASHFS_GUIDS];
210
211 /***
212 * the root inode number.
213 */
214 private long root_inode_number = 0;
215
216 /***
217 *
218 */
219 private long bytes;
220
221 /***
222 * the manifest.
223 */
224 private Manifest manifest;
225
226 /***
227 * the destination.
228 */
229 private IRandomAccessSource dest;
230
231 /***
232 * the easy out.
233 */
234 private EasyIOOutputStream out;
235
236 /***
237 *
238 */
239 private squashfs_fragment_entry[] fragment_table;
240
241 /***
242 *
243 */
244 private boolean be;
245
246 /***
247 *
248 */
249 private boolean noI;
250
251 /***
252 *
253 */
254 private boolean noD;
255
256 /***
257 *
258 */
259 private boolean check_data;
260
261 /***
262 *
263 */
264 private boolean noF;
265
266 /***
267 *
268 */
269 private boolean no_fragments;
270
271 /***
272 *
273 */
274 private boolean always_use_fragments;
275
276 /***
277 *
278 */
279 private boolean duplicate_checking;
280
281 /***
282 *
283 */
284 private boolean nopad;
285
286 /***
287 *
288 */
289 private int block_offset;
290
291 /***
292 *
293 */
294 private int dir_inode_no = 1;
295
296 /***
297 *
298 */
299
300 /***
301 *
302 */
303
304 /***
305 *
306 */
307 private int inode_no;
308
309 /***
310 *
311 */
312 private int directory_size;
313
314 /***
315 *
316 */
317 private int cache_bytes;
318
319 /***
320 *
321 */
322 private byte[] inode_table;
323
324 /***
325 *
326 */
327 private int inode_size;
328
329 /***
330 * flag whether destination file is a block device.
331 */
332 private int block_device = 0;
333
334 /***
335 *
336 */
337 private byte[] data_cache;
338
339 /***
340 *
341 */
342 private int fragment_size;
343
344 /***
345 *
346 */
347 private byte[] fragment_data = new byte[(int) squashfs_constants.SQUASHFS_FILE_SIZE];
348
349 /***
350 *
351 */
352 private int total_uncompressed;
353
354 /***
355 *
356 */
357 private int total_compressed;
358
359 /***
360 *
361 */
362 private DataProvider dataProvider;
363
364 /***
365 *
366 */
367 private long cache_size = 0;
368
369 /***
370 *
371 */
372 private int directory_cache_size = 0;
373
374 /***
375 *
376 */
377 private boolean swap = false;
378
379 /***
380 * the dev where the file resides.
381 */
382 private int st_dev = 1;
383
384 /***
385 *
386 */
387 private byte[] cached_fragment = new byte[(int) squashfs_constants.SQUASHFS_FILE_SIZE];
388
389 /***
390 *
391 */
392 private int cached_frag1 = -1;
393
394 /***
395 *
396 */
397 private file_info[] dupl = new file_info[65536];
398
399 /***
400 *
401 */
402 private file_info[] frag_dups = new file_info[65536];
403
404 /***
405 * constructor.
406 * @param manifest the manifest.
407 * @param dataProvider the data provider.
408 */
409 public SquashFSWriter(Manifest manifest, DataProvider dataProvider) {
410 this.dataProvider = dataProvider;
411 this.manifest = manifest;
412 this.be = false;
413 this.noI = true;
414 this.noD = true;
415 this.noF = true;
416 this.check_data = false;
417 this.no_fragments = false;
418 this.always_use_fragments = false;
419 this.duplicate_checking = true;
420 this.nopad = false;
421 }
422
423 /***
424 * writes the directory to the destination file.
425 * @param dir the directory to write.
426 * @param randDest the destination.
427 * @throws IOException
428 * @throws EasyIOException
429 * @throws EasyIOException
430 * @throws SquashFSException
431 */
432 public void write(IRandomAccessSource randDest) throws IOException,
433 EasyIOException, SquashFSException {
434 this.dest = randDest;
435 this.dest.setLength(0);
436 this.out = new EasyIOOutputStream(this.dest);
437
438 this.block_offset = this.check_data ? 3 : 2;
439 this.total_bytes = 0;
440 this.uid_count = 0;
441 this.total_inode_bytes = 0;
442 this.total_directory_bytes = 0;
443 this.guid_count = 0;
444
445 this.superBlock = new squashfs_super_block();
446 this.superBlock.inodes = 0;
447 this.superBlock.s_magic = squashfs_constants.SQUASHFS_MAGIC;
448 this.superBlock.s_major = squashfs_constants.SQUASHFS_MAJOR;
449 this.superBlock.s_minor = squashfs_constants.SQUASHFS_MINOR;
450 this.superBlock.block_size = squashfs_constants.SQUASHFS_FILE_SIZE;
451 this.superBlock.block_log = squashfs_constants
452 .slog(this.superBlock.block_size);
453 this.superBlock.flags = squashfs_constants.SQUASHFS_MKFLAGS(this.noI,
454 this.noD, this.check_data, this.noF, this.no_fragments,
455 this.always_use_fragments, this.duplicate_checking);
456 this.superBlock.mkfs_time = SquashFSUtils.getMTimeFromDate(system
457 .getCurrentDate());
458 this.superBlock.fragments = 0;
459
460 log.info("Creating " + (this.be ? "big endian" : "little endian") + " "
461 + squashfs_constants.SQUASHFS_MAJOR + "."
462 + squashfs_constants.SQUASHFS_MINOR + " filesystem on "
463 + this.dest.toString() + ", block size "
464 + this.superBlock.block_size + ".");
465 this.bytes = squashfs_constants.SQUASHFS_SUPER_BLOCK_SIZE;
466
467 long inode = dir_scan(this.manifest.getRoot());
468 this.superBlock.root_inode = inode;
469
470 write_fragment();
471 this.superBlock.inode_table_start = write_inodes();
472 this.superBlock.directory_table_start = write_directories();
473 this.superBlock.fragment_table_start = write_fragment_table();
474
475 log.trace("sBlk->inode_table_start 0x"
476 + Long.toHexString(this.superBlock.inode_table_start));
477 log.trace("sBlk->directory_table_start 0x"
478 + Long.toHexString(this.superBlock.directory_table_start));
479 log.trace("sBlk->fragment_table_start 0x"
480 + Long.toHexString(this.superBlock.fragment_table_start));
481
482 if ((this.superBlock.no_uids = this.uid_count) != 0) {
483
484 this.dest.seek(this.bytes);
485 for (int i = 0; i < this.uid_count; i++) {
486 this.out.writeUINT32(this.uids[i]);
487 }
488
489
490
491
492
493
494
495
496 this.superBlock.uid_start = this.bytes;
497 this.bytes += this.uid_count * squashfs_constants.SQUASHFS_UID_SIZE;
498 } else {
499 this.superBlock.uid_start = 0;
500 }
501
502 if ((this.superBlock.no_guids = this.guid_count) != 0) {
503
504 this.dest.seek(this.bytes);
505 for (int i = 0; i < this.guid_count; i++) {
506 this.out.writeUINT32(this.guids[i]);
507 }
508
509
510
511
512
513
514
515
516 this.superBlock.guid_start = this.bytes;
517 this.bytes += this.guid_count
518 * squashfs_constants.SQUASHFS_UID_SIZE;
519 } else {
520 this.superBlock.guid_start = 0;
521 }
522
523 this.superBlock.bytes_used = this.bytes;
524 this.superBlock.inodes = this.inode_count;
525 this.superBlock.unused = new BigInteger("ffffffffffffffff", 16);
526
527
528
529
530 this.dest.seek(squashfs_constants.SQUASHFS_START);
531 this.out.write(this.superBlock);
532
533
534
535
536
537
538
539
540
541 long i;
542 if (!this.nopad && (i = (this.bytes & (4096 - 1))) != 0) {
543 byte[] temp = new byte[4096];
544 this.dest.seek(this.bytes);
545 this.out.write(temp, 0, (int) (4096 - i));
546 }
547
548 this.total_bytes += this.total_inode_bytes + this.total_directory_bytes
549 + (this.uid_count * 4) + (this.guid_count * 4)
550 + squashfs_constants.SQUASHFS_SUPER_BLOCK_SIZE;
551
552 log.debug((this.be ? "Big endian" : "Little endian")
553 + " filesystem, data block size "
554 + this.superBlock.block_size
555 + ", "
556 + (this.noD ? "uncompressed" : "compressed")
557 + " data, "
558 + (this.noI ? "uncompressed" : "compressed")
559 + " metadata, "
560 + (this.no_fragments ? "no" : this.noF ? "uncompressed"
561 : "compressed") + " fragments");
562 log.debug("Filesystem size "
563 + PERCENT_FORMAT.format(this.bytes / 1024.0) + " Kbytes ("
564 + PERCENT_FORMAT.format(this.bytes / (1024.0 * 1024.0))
565 + " Mbytes)");
566 log
567 .debug("\t"
568 + PERCENT_FORMAT
569 .format(((float) this.bytes / this.total_bytes) * 100.0)
570 + "% of uncompressed filesystem size ("
571 + PERCENT_FORMAT.format(this.total_bytes / 1024.0)
572 + " Kbytes)");
573 log
574 .debug("Inode table size " + this.inode_bytes + " bytes ("
575 + PERCENT_FORMAT.format(this.inode_bytes / 1024.0)
576 + " Kbytes)");
577 log
578 .debug("\t"
579 + PERCENT_FORMAT
580 .format(((float) this.inode_bytes / this.total_inode_bytes) * 100.0)
581 + "% of uncompressed inode table size ("
582 + this.total_inode_bytes + " bytes)");
583 log.debug("Directory table size " + this.directory_bytes + " bytes ("
584 + PERCENT_FORMAT.format(this.directory_bytes / 1024.0)
585 + " Kbytes)");
586 log
587 .debug("\t"
588 + PERCENT_FORMAT
589 .format(((float) this.directory_bytes / this.total_directory_bytes) * 100.0)
590 + "% of uncompressed directory table size ("
591 + (this.total_directory_bytes) + " bytes)");
592 if (this.duplicate_checking) {
593 log.debug("Number of duplicate files found "
594 + (this.file_count - this.dup_files));
595 } else {
596 log.debug("No duplicate files removed");
597 }
598 log.debug("Number of inodes " + this.inode_count);
599 log.debug("Number of files " + this.file_count);
600 if (!this.no_fragments) {
601 log.debug("Number of fragments " + this.superBlock.fragments);
602 }
603 log.debug("Number of symbolic links " + this.sym_count);
604 log.debug("Number of device nodes " + this.dev_count);
605 log.debug("Number of fifo nodes " + this.fifo_count);
606 log.debug("Number of socket nodes " + this.sock_count);
607 log.debug("Number of directories " + this.dir_count);
608 log.debug("Number of uids " + this.uid_count);
609
610 for (i = 0; i < this.uid_count; i++) {
611 String userName = getpwuid(this.uids[(int) i]);
612 log.debug("\t" + userName + " (" + this.uids[(int) i] + ")");
613 }
614
615 log.debug("Number of gids " + this.guid_count);
616
617 for (i = 0; i < this.guid_count; i++) {
618 String userName = getgrgid(this.guids[(int) i]);
619 log.debug("\t" + userName + " (" + this.guids[(int) i] + ")");
620 }
621 }
622
623 /***
624 * @param l
625 * @return z.
626 */
627 private String getgrgid(long l) {
628 if (l == 0) {
629 return "root";
630 }
631
632 return "";
633 }
634
635 /***
636 * @param l
637 * @return z.
638 */
639 private String getpwuid(long l) {
640 if (l == 0) {
641 return "root";
642 }
643
644 return "";
645 }
646
647 /***
648 * @param squashfs
649 * @return start.
650 * @throws IOException
651 * @throws EasyIOException
652 */
653 private long write_fragment_table() throws IOException, EasyIOException {
654 long frag_bytes = squashfs_constants
655 .SQUASHFS_FRAGMENT_BYTES(this.superBlock.fragments);
656 long meta_blocks = squashfs_constants
657 .SQUASHFS_FRAGMENT_INDEXES(this.superBlock.fragments);
658 long start_bytes = 0;
659 long[] list = new long[(int) meta_blocks];
660 int compressed_size;
661 int c_byte;
662 byte[] cbuffer = new byte[(squashfs_constants.SQUASHFS_METADATA_SIZE << 2) + 2];
663 RandomAccessByteArray cbuffer_rand = new RandomAccessByteArray(cbuffer);
664 EasyIOOutputStream cbuffer_out = new EasyIOOutputStream(
665 new RandomAccessByteArrayOutputStreamAdapter(cbuffer_rand));
666 byte[] buffer = new byte[(int) frag_bytes];
667 RandomAccessByteArray buffer_rand = new RandomAccessByteArray(buffer);
668 EasyIOOutputStream buffer_out = new EasyIOOutputStream(buffer_rand);
669
670
671
672
673 log.trace("write_fragment_table: fragments "
674 + this.superBlock.fragments + ", frag_bytes " + frag_bytes
675 + ", meta_blocks " + meta_blocks);
676 int p = 0;
677 for (int i = 0; i < this.superBlock.fragments; i++, p += squashfs_constants.SQUASHFS_FRAGMENT_ENTRY_SIZE) {
678 log.trace("write_fragment_table: fragment " + i + ", start_block "
679 + Long.toHexString(this.fragment_table[i].start_block)
680 + ", size " + this.fragment_table[i].size);
681
682
683 buffer_rand.seek(p);
684 buffer_out.write(this.fragment_table[i]);
685
686
687
688 }
689 for (int i = 0; i < meta_blocks; i++) {
690 int avail_bytes = (int) (i == meta_blocks - 1 ? frag_bytes
691 % squashfs_constants.SQUASHFS_METADATA_SIZE
692 : squashfs_constants.SQUASHFS_METADATA_SIZE);
693 cbuffer_rand.seek(this.block_offset);
694 buffer_rand.seek(i * squashfs_constants.SQUASHFS_METADATA_SIZE);
695 c_byte = (int) mangle(cbuffer_out, buffer_rand, avail_bytes,
696 squashfs_constants.SQUASHFS_METADATA_SIZE, this.noF, 0);
697
698 cbuffer_rand.seek(0);
699 cbuffer_out.writeUINT16(c_byte);
700
701
702
703 if (this.check_data) {
704 cbuffer[this.block_offset - 1] = squashfs_constants.SQUASHFS_MARKER_BYTE;
705 }
706 list[i] = this.bytes;
707 compressed_size = squashfs_constants
708 .SQUASHFS_COMPRESSED_SIZE(c_byte)
709 + this.block_offset;
710 this.dest.seek(this.bytes);
711 this.dest.write(cbuffer, 0, compressed_size);
712 this.bytes += compressed_size;
713 }
714
715
716 this.dest.seek(this.bytes);
717 for (int i = 0; i < list.length; i++) {
718 this.out.writeINT64(list[i]);
719 }
720
721
722
723
724
725
726 start_bytes = this.bytes;
727 this.bytes += squashfs_constants.SQUASHFS_FRAGMENT_INDEX_SIZE
728 * meta_blocks;
729
730 return start_bytes;
731 }
732
733 /***
734 * @param d
735 * @param s
736 * @param size
737 * @param block_size
738 * @param uncompressed
739 * @param data_block
740 * @return z
741 * @throws IOException
742 */
743 private long mangle(EasyIOOutputStream d, RandomAccessByteArray s,
744 int size, int block_size, boolean uncompressed, int data_block)
745 throws IOException {
746 long c_byte = block_size << 1;
747
748 byte[] readdata = new byte[size];
749 s.read(readdata);
750 if (!uncompressed) {
751 long start = d.getCount();
752 DeflaterOutputStream o = new DeflaterOutputStream(d);
753 o.write(readdata);
754 o.close();
755 c_byte = d.getCount() - start;
756 }
757
758 if (uncompressed || c_byte >= size) {
759 d.write(readdata);
760 return size
761 | (data_block != 0 ? squashfs_constants.SQUASHFS_COMPRESSED_BIT_BLOCK
762 : squashfs_constants.SQUASHFS_COMPRESSED_BIT);
763 }
764
765 return c_byte;
766 }
767
768 /***
769 * @param squashfs
770 * @return start.
771 * @throws IOException
772 */
773 private long write_directories() throws IOException {
774 long start_bytes = this.bytes;
775
776 int c_byte;
777 int avail_bytes;
778 int directoryp = 0;
779
780 while (this.directory_cache_bytes != 0) {
781 if (this.directory_size - this.directory_bytes < ((squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2)) {
782 this.directory_table = realloc(
783 this.directory_table,
784 this.directory_size
785 + ((squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2));
786 this.directory_size += (squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2;
787 }
788 avail_bytes = this.directory_cache_bytes > squashfs_constants.SQUASHFS_METADATA_SIZE ? squashfs_constants.SQUASHFS_METADATA_SIZE
789 : this.directory_cache_bytes;
790 RandomAccessByteArray directory_table_array = new RandomAccessByteArray(
791 this.directory_table);
792 EasyIOOutputStream directory_table_stream = new EasyIOOutputStream(
793 new RandomAccessByteArrayOutputStreamAdapter(
794 directory_table_array));
795 RandomAccessByteArray directory_data_cache_array = new RandomAccessByteArray(
796 this.directory_data_cache);
797 directory_data_cache_array.seek(directoryp);
798 directory_table_array
799 .seek(this.directory_bytes + this.block_offset);
800 c_byte = (int) mangle(directory_table_stream,
801 directory_data_cache_array, avail_bytes,
802 squashfs_constants.SQUASHFS_METADATA_SIZE, this.noI, 0);
803 log.trace("Directory block @ " + this.directory_bytes + ", size "
804 + c_byte);
805
806 directory_table_array.seek(this.directory_bytes);
807 directory_table_stream.writeUINT16(c_byte);
808
809
810
811 if (this.check_data) {
812 this.directory_table[this.directory_bytes + this.block_offset
813 - 1] = squashfs_constants.SQUASHFS_MARKER_BYTE;
814 }
815 this.directory_bytes += squashfs_constants
816 .SQUASHFS_COMPRESSED_SIZE(c_byte)
817 + this.block_offset;
818 this.total_directory_bytes += avail_bytes + this.block_offset;
819 directoryp += avail_bytes;
820 this.directory_cache_bytes -= avail_bytes;
821 }
822 this.dest.seek(this.bytes);
823 this.dest.write(this.directory_table, 0, this.directory_bytes);
824 this.bytes += this.directory_bytes;
825
826 return start_bytes;
827 }
828
829 /***
830 * @param prev
831 * @param new_size
832 * @return the new buffer.
833 */
834 private byte[] realloc(byte[] prev, int new_size) {
835 byte[] results = new byte[new_size];
836 if (prev != null) {
837 System.arraycopy(prev, 0, results, 0, prev.length);
838 }
839 return results;
840 }
841
842 /***
843 * @param prev
844 * @param new_size
845 * @return the new array.
846 */
847 private squashfs_fragment_entry[] realloc(squashfs_fragment_entry[] prev,
848 int new_size) {
849 squashfs_fragment_entry[] results = new squashfs_fragment_entry[new_size];
850 if (prev != null) {
851 System.arraycopy(prev, 0, results, 0, prev.length);
852 }
853 return results;
854 }
855
856 /***
857 * @param prev
858 * @param new_size
859 * @return the new array.
860 */
861 private cached_dir_index[] realloc(cached_dir_index[] prev, int new_size) {
862 cached_dir_index[] results = new cached_dir_index[new_size];
863 System.arraycopy(prev, 0, results, 0, prev.length);
864 return results;
865 }
866
867 /***
868 * @param squashfs
869 * @return start.
870 * @throws IOException
871 */
872 private long write_inodes() throws IOException {
873 int c_byte;
874 int avail_bytes;
875 int datap = 0;
876 long start_bytes = this.bytes;
877
878 while (this.cache_bytes != 0) {
879 if (this.inode_size - this.inode_bytes < ((squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2)) {
880 this.inode_table = realloc(
881 this.inode_table,
882 this.inode_size
883 + ((squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2));
884 this.inode_size += (squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2;
885 }
886 avail_bytes = this.cache_bytes > squashfs_constants.SQUASHFS_METADATA_SIZE ? squashfs_constants.SQUASHFS_METADATA_SIZE
887 : this.cache_bytes;
888 RandomAccessByteArray inode_table_array = new RandomAccessByteArray(
889 this.inode_table);
890 EasyIOOutputStream inode_table_stream = new EasyIOOutputStream(
891 new RandomAccessByteArrayOutputStreamAdapter(
892 inode_table_array));
893 RandomAccessByteArray data_cache_array = new RandomAccessByteArray(
894 this.data_cache);
895 inode_table_array.seek(this.inode_bytes + this.block_offset);
896 data_cache_array.seek(datap);
897 c_byte = (int) mangle(inode_table_stream, data_cache_array,
898 avail_bytes, squashfs_constants.SQUASHFS_METADATA_SIZE,
899 this.noI, 0);
900 log.trace("Inode block @ " + this.inode_bytes + ", size " + c_byte);
901
902 inode_table_array.seek(this.inode_bytes);
903 inode_table_stream.writeUINT16(c_byte);
904
905
906
907 if (this.check_data) {
908 this.inode_table[this.inode_bytes + this.block_offset - 1] = squashfs_constants.SQUASHFS_MARKER_BYTE;
909 }
910 this.inode_bytes += squashfs_constants
911 .SQUASHFS_COMPRESSED_SIZE(c_byte)
912 + this.block_offset;
913 this.total_inode_bytes += avail_bytes + this.block_offset;
914 datap += avail_bytes;
915 this.cache_bytes -= avail_bytes;
916 }
917
918 this.dest.seek(this.bytes);
919 this.out.write(this.inode_table, 0, this.inode_bytes);
920 this.bytes += this.inode_bytes;
921
922 return start_bytes;
923 }
924
925 /***
926 * @param squashfs
927 * @throws IOException
928 */
929 private void write_fragment() throws IOException {
930 int compressed_size;
931 byte[] buffer = new byte[(int) (this.superBlock.block_size << 1)];
932 RandomAccessByteArray fragment_data_array = new RandomAccessByteArray(
933 this.fragment_data);
934 RandomAccessByteArray buffer_array = new RandomAccessByteArray(buffer);
935 EasyIOOutputStream buffer_stream = new EasyIOOutputStream(
936 new RandomAccessByteArrayOutputStreamAdapter(buffer_array));
937
938 if (this.fragment_size == 0) {
939 return;
940 }
941
942 if (this.superBlock.fragments % FRAG_SIZE == 0) {
943 this.fragment_table = realloc(this.fragment_table,
944 ((int) this.superBlock.fragments + FRAG_SIZE)
945 * squashfs_constants.SQUASHFS_FRAGMENT_ENTRY_SIZE);
946 }
947 if (this.fragment_table[(int) this.superBlock.fragments] == null) {
948 this.fragment_table[(int) this.superBlock.fragments] = new squashfs_fragment_entry();
949 }
950 this.fragment_table[(int) this.superBlock.fragments].size = mangle(
951 buffer_stream, fragment_data_array, this.fragment_size,
952 (int) this.superBlock.block_size, this.noF, 1);
953 this.fragment_table[(int) this.superBlock.fragments].start_block = this.bytes;
954 compressed_size = (int) squashfs_constants
955 .SQUASHFS_COMPRESSED_SIZE_BLOCK(this.fragment_table[(int) this.superBlock.fragments].size);
956 this.dest.seek(this.bytes);
957 this.out.write(buffer, 0, compressed_size);
958 this.bytes += compressed_size;
959 this.total_uncompressed += this.fragment_size;
960 this.total_compressed += compressed_size;
961 log.trace("Writing fragment " + this.superBlock.fragments
962 + ", uncompressed size " + this.fragment_size
963 + ", compressed size " + compressed_size);
964 this.superBlock.fragments++;
965 this.fragment_size = 0;
966 }
967
968 /***
969 * @param squashfs
970 * @param dir
971 * @return the root inode.
972 * @throws SquashFSException
973 * @throws IOException
974 * @throws EasyIOException
975 */
976 private long dir_scan(Directory dir) throws SquashFSException, IOException,
977 EasyIOException {
978 dir_info dir_info = dir_scan1(dir, ".");
979 dir_ent dir_ent;
980 inode_info inode_info_var;
981
982 if (dir_info == null) {
983 return -1;
984 }
985
986 dir_ent = new dir_ent();
987 inode_info_var = new inode_info();
988
989 dir_ent.name = dir_ent.pathname = dir.getName();
990 dir_ent.dir = dir_info;
991 dir_ent.inode = inode_info_var;
992 dir_ent.our_dir = null;
993 dir_ent.data = null;
994 inode_info_var.nlink = 1;
995 inode_info_var.inode_number = this.root_inode_number != 0 ? this.root_inode_number
996 : this.dir_inode_no++;
997 dir_info.dir_ent = dir_ent;
998 inode_info_var.buf = new stat();
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011 inode_info_var.buf = fromBaseFile(dir);
1012
1013
1014
1015
1016
1017
1018
1019
1020 return dir_scan2(dir_info);
1021 }
1022
1023 /***
1024 * @param dir_info
1025 * @return the inode
1026 * @throws SquashFSException
1027 * @throws IOException
1028 * @throws EasyIOException
1029 */
1030 private long dir_scan2(dir_info dir_info) throws SquashFSException,
1031 IOException, EasyIOException {
1032 int squashfs_type;
1033 boolean[] duplicate_file;
1034 String pathname = dir_info.pathname;
1035 directory dir = new directory();
1036 dir_ent dir_ent;
1037 long inode = 0;
1038
1039 scan2_init_dir(dir);
1040
1041 while ((dir_ent = scan2_readdir(dir, dir_info)) != null) {
1042 inode_info inode_info_var = dir_ent.inode;
1043 stat buf = inode_info_var.buf;
1044 String filename = dir_ent.pathname;
1045 String dir_name = dir_ent.name;
1046 long inode_number = ((buf.st_mode & stat.S_IFMT) == stat.S_IFDIR) ? dir_ent.inode.inode_number
1047 : dir_ent.inode.inode_number + this.dir_inode_no;
1048
1049 if (dir_ent.inode.inode == squashfs_constants.SQUASHFS_INVALID_BLK) {
1050 switch ((int) (buf.st_mode & stat.S_IFMT)) {
1051 case stat.S_IFREG:
1052 squashfs_type = squashfs_constants.SQUASHFS_FILE_TYPE;
1053 duplicate_file = new boolean[] { false };
1054 inode = write_file(dir_ent, buf.st_size, duplicate_file);
1055 log.info("file " + filename + ", uncompressed size "
1056 + buf.st_size + " bytes "
1057 + (duplicate_file[0] ? "DUPLICATE" : ""));
1058 break;
1059
1060 case stat.S_IFDIR:
1061 squashfs_type = squashfs_constants.SQUASHFS_DIR_TYPE;
1062 inode = dir_scan2(dir_ent.dir);
1063 break;
1064
1065 case stat.S_IFLNK:
1066 squashfs_type = squashfs_constants.SQUASHFS_SYMLINK_TYPE;
1067 inode = create_inode(dir_ent, squashfs_type, 0, 0, 0, null,
1068 0, null, null);
1069 log.info("symbolic link " + dir_name + " inode 0x"
1070 + Long.toHexString(inode));
1071 this.sym_count++;
1072 break;
1073
1074 case stat.S_IFCHR:
1075 squashfs_type = squashfs_constants.SQUASHFS_CHRDEV_TYPE;
1076 inode = create_inode(dir_ent, squashfs_type, 0, 0, 0, null,
1077 0, null, null);
1078 log
1079 .info("character device " + dir_name + " inode "
1080 + inode);
1081 this.dev_count++;
1082 break;
1083
1084 case stat.S_IFBLK:
1085 squashfs_type = squashfs_constants.SQUASHFS_BLKDEV_TYPE;
1086 inode = create_inode(dir_ent, squashfs_type, 0, 0, 0, null,
1087 0, null, null);
1088 log.info("block device " + dir_name + " inode " + inode);
1089 this.dev_count++;
1090 break;
1091
1092 case stat.S_IFIFO:
1093 squashfs_type = squashfs_constants.SQUASHFS_FIFO_TYPE;
1094 inode = create_inode(dir_ent, squashfs_type, 0, 0, 0, null,
1095 0, null, null);
1096 log.info("fifo " + dir_name + " inode " + inode);
1097 this.fifo_count++;
1098 break;
1099
1100 case stat.S_IFSOCK:
1101 squashfs_type = squashfs_constants.SQUASHFS_SOCKET_TYPE;
1102 inode = create_inode(dir_ent, squashfs_type, 0, 0, 0, null,
1103 0, null, null);
1104 log.info("unix domain socket " + dir_name + " inode "
1105 + inode);
1106 this.sock_count++;
1107 break;
1108
1109 default:
1110 throw new SquashFSException(filename
1111 + " unrecognised file type, mode is " + buf.st_mode);
1112 }
1113 dir_ent.inode.inode = inode;
1114 dir_ent.inode.type = squashfs_type;
1115 } else {
1116 inode = dir_ent.inode.inode;
1117 squashfs_type = dir_ent.inode.type;
1118 switch (squashfs_type) {
1119 case squashfs_constants.SQUASHFS_FILE_TYPE:
1120
1121 log.info("file " + filename + ", uncompressed size "
1122 + buf.st_size + " bytes LINK");
1123
1124 break;
1125 case squashfs_constants.SQUASHFS_SYMLINK_TYPE:
1126 log.info("symbolic link " + dir_name + " inode " + inode
1127 + " LINK");
1128 break;
1129 case squashfs_constants.SQUASHFS_CHRDEV_TYPE:
1130 log.info("character device " + dir_name + " inode " + inode
1131 + " LINK");
1132 break;
1133 case squashfs_constants.SQUASHFS_BLKDEV_TYPE:
1134 log.info("block device " + dir_name + " inode " + inode
1135 + " LINK");
1136 break;
1137 case squashfs_constants.SQUASHFS_FIFO_TYPE:
1138 log.info("fifo " + dir_name + " inode " + inode + " LINK");
1139 break;
1140 case squashfs_constants.SQUASHFS_SOCKET_TYPE:
1141 log.info("unix domain socket " + dir_name + " inode "
1142 + inode + " LINK");
1143 break;
1144 }
1145 }
1146
1147 add_dir(inode, inode_number, dir_name, squashfs_type, dir);
1148 }
1149
1150 inode = write_dir(dir_info, dir);
1151 log.info("directory " + pathname + " inode 0x"
1152 + Long.toHexString(inode));
1153
1154 scan2_freedir(dir);
1155
1156 return inode;
1157 }
1158
1159 /***
1160 * @param dir
1161 */
1162 private void scan2_freedir(directory dir) {
1163
1164 }
1165
1166 /***
1167 * @param dir_info
1168 * @param dir
1169 * @return z.
1170 * @throws IOException
1171 * @throws EasyIOException
1172 * @throws SquashFSException
1173 */
1174 private long write_dir(dir_info dir_info, directory dir)
1175 throws EasyIOException, IOException, SquashFSException {
1176 long dir_size = dir.buffp;
1177 int data_space = (this.directory_cache_size - this.directory_cache_bytes);
1178 long directory_block, directory_offset, i_count, index;
1179 int c_byte;
1180
1181 if (data_space < dir_size) {
1182 int realloc_size = (int) (this.directory_cache_size == 0 ? ((dir_size + squashfs_constants.SQUASHFS_METADATA_SIZE) & ~(squashfs_constants.SQUASHFS_METADATA_SIZE - 1))
1183 : dir_size - data_space);
1184
1185 this.directory_data_cache = realloc(this.directory_data_cache,
1186 this.directory_cache_size + realloc_size);
1187 this.directory_cache_size += realloc_size;
1188 }
1189
1190 if (dir_size != 0) {
1191 squashfs_dir_header dir_header = new squashfs_dir_header();
1192
1193 dir_header.count = dir.entry_count - 1;
1194 dir_header.start_block = dir.start_block;
1195 dir_header.inode_number = dir.inode_number;
1196
1197 RandomAccessByteArray buff_array = new RandomAccessByteArray(
1198 dir.buff);
1199 EasyIOOutputStream buff_stream = new EasyIOOutputStream(
1200 new RandomAccessByteArrayOutputStreamAdapter(buff_array));
1201 buff_array.seek(dir.entry_count_p);
1202 buff_stream.write(dir_header);
1203
1204
1205
1206 RandomAccessByteArray directory_data_cache_array = new RandomAccessByteArray(
1207 this.directory_data_cache);
1208 directory_data_cache_array.seek(this.directory_cache_bytes);
1209 directory_data_cache_array.write(dir.buff, 0, (int) dir_size);
1210 }
1211
1212 directory_offset = this.directory_cache_bytes;
1213 directory_block = this.directory_bytes;
1214 this.directory_cache_bytes += dir_size;
1215 i_count = 0;
1216 index = squashfs_constants.SQUASHFS_METADATA_SIZE - directory_offset;
1217
1218 while (true) {
1219 while (i_count < dir.i_count
1220 && dir.index[(int) i_count].index.index < index) {
1221 dir.index[(int) i_count++].index.start_block = this.directory_bytes;
1222 }
1223 index += squashfs_constants.SQUASHFS_METADATA_SIZE;
1224
1225 if (this.directory_cache_bytes < squashfs_constants.SQUASHFS_METADATA_SIZE) {
1226 break;
1227 }
1228
1229 if ((this.directory_size - this.directory_bytes) < ((squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2)) {
1230 this.directory_table = realloc(
1231 this.directory_table,
1232 this.directory_size
1233 + (squashfs_constants.SQUASHFS_METADATA_SIZE << 1)
1234 + 2);
1235 this.directory_size += squashfs_constants.SQUASHFS_METADATA_SIZE << 1;
1236 }
1237
1238 RandomAccessByteArray directory_table_array = new RandomAccessByteArray(
1239 this.directory_table);
1240 EasyIOOutputStream directory_table_stream = new EasyIOOutputStream(
1241 new RandomAccessByteArrayOutputStreamAdapter(
1242 directory_table_array));
1243 RandomAccessByteArray directory_data_cache_array = new RandomAccessByteArray(
1244 this.directory_data_cache);
1245 directory_table_array
1246 .seek(this.directory_bytes + this.block_offset);
1247 c_byte = (int) mangle(directory_table_stream,
1248 directory_data_cache_array,
1249 squashfs_constants.SQUASHFS_METADATA_SIZE,
1250 squashfs_constants.SQUASHFS_METADATA_SIZE, this.noI, 0);
1251 log.trace("Directory block @ " + this.directory_bytes + ", size "
1252 + c_byte);
1253
1254 directory_table_array = new RandomAccessByteArray(
1255 this.directory_table);
1256 directory_table_stream = new EasyIOOutputStream(
1257 new RandomAccessByteArrayOutputStreamAdapter(
1258 directory_table_array));
1259 directory_table_stream.writeUINT16(c_byte);
1260
1261
1262
1263 if (this.check_data) {
1264 this.directory_table[this.directory_bytes + this.block_offset
1265 - 1] = squashfs_constants.SQUASHFS_MARKER_BYTE;
1266 }
1267 this.directory_bytes += squashfs_constants
1268 .SQUASHFS_COMPRESSED_SIZE(c_byte)
1269 + this.block_offset;
1270 this.total_directory_bytes += squashfs_constants.SQUASHFS_METADATA_SIZE
1271 + this.block_offset;
1272 directory_data_cache_array = new RandomAccessByteArray(
1273 this.directory_data_cache);
1274 directory_data_cache_array.write(this.directory_data_cache,
1275 squashfs_constants.SQUASHFS_METADATA_SIZE,
1276 this.directory_cache_bytes
1277 - squashfs_constants.SQUASHFS_METADATA_SIZE);
1278 this.directory_cache_bytes -= squashfs_constants.SQUASHFS_METADATA_SIZE;
1279 }
1280
1281 long inode;
1282 if (dir_info.dir_is_ldir) {
1283 inode = create_inode(dir_info.dir_ent,
1284 squashfs_constants.SQUASHFS_LDIR_TYPE, dir_size + 3,
1285 directory_block, directory_offset, null, 0, null, dir);
1286 } else {
1287 inode = create_inode(dir_info.dir_ent,
1288 squashfs_constants.SQUASHFS_DIR_TYPE, dir_size + 3,
1289 directory_block, directory_offset, null, 0, null, null);
1290 }
1291
1292 if (log.isTraceEnabled()) {
1293 RandomAccessByteArray dirbuff_array = new RandomAccessByteArray(
1294 dir.buff);
1295 EasyIOInputStream dirbuff_stream = new EasyIOInputStream(
1296 new RandomAccessByteArrayInputStreamAdaptor(dirbuff_array));
1297 if (!this.swap) {
1298 long dirbuffp;
1299 int count;
1300
1301 log.trace("Directory contents of inode 0x"
1302 + Long.toHexString(inode));
1303 dirbuffp = 0;
1304 while (dirbuffp < dir.buffp) {
1305 byte[] buffer = new byte[squashfs_constants.SQUASHFS_NAME_LEN + 1];
1306 squashfs_dir_entry idir;
1307 dirbuff_array.seek((int) dirbuffp);
1308 squashfs_dir_header dirh = dirbuff_stream
1309 .read(new squashfs_dir_header());
1310 count = dirh.count + 1;
1311 dirbuffp += squashfs_constants.SQUASHFS_DIR_HEADER_SIZE;
1312
1313 log.trace("\tStart block 0x"
1314 + Long.toHexString(dirh.start_block) + ", count "
1315 + count);
1316
1317 while (count-- != 0) {
1318 dirbuff_array.seek((int) dirbuffp);
1319 idir = dirbuff_stream.read(new squashfs_dir_entry());
1320 dirbuff_array.read(buffer, 0, idir.size + 1);
1321 log.trace("\t\tname "
1322 + new String(buffer, 0, idir.size + 1)
1323 + ", inode offset 0x"
1324 + Long.toHexString(idir.offset) + ", type "
1325 + idir.type);
1326 dirbuffp += squashfs_constants.SQUASHFS_DIR_ENTRY_SIZE
1327 + idir.size + 1;
1328 }
1329 }
1330 }
1331 }
1332
1333 this.dir_count++;
1334
1335 return inode;
1336 }
1337
1338 /***
1339 * @param inode
1340 * @param inode_number
1341 * @param name
1342 * @param type
1343 * @param dir
1344 * @throws IOException
1345 * @throws EasyIOException
1346 */
1347 private void add_dir(long inode, long inode_number, String name, int type,
1348 directory dir) throws EasyIOException, IOException {
1349 long size;
1350 long start_block = inode >> 16;
1351 squashfs_dir_entry idir = new squashfs_dir_entry();
1352 long offset = inode & 0xffff;
1353
1354 if ((size = name.length()) > squashfs_constants.SQUASHFS_NAME_LEN) {
1355 size = squashfs_constants.SQUASHFS_NAME_LEN;
1356 log.error("Filename is greater than "
1357 + squashfs_constants.SQUASHFS_NAME_LEN
1358 + " characters, truncating! ...");
1359 }
1360
1361 if (dir.buffp + squashfs_constants.SQUASHFS_DIR_ENTRY_SIZE + size
1362 + squashfs_constants.SQUASHFS_DIR_HEADER_SIZE >= dir.size) {
1363 dir.buff = realloc(dir.buff,
1364 dir.size += squashfs_constants.SQUASHFS_METADATA_SIZE);
1365 }
1366
1367 if (dir.entry_count == 256
1368 || start_block != dir.start_block
1369 || ((dir.entry_count_p >= 0) && ((dir.buffp
1370 + squashfs_constants.SQUASHFS_DIR_ENTRY_SIZE + size - dir.index_count_p) > squashfs_constants.SQUASHFS_METADATA_SIZE))
1371 || ((long) inode_number - dir.inode_number) > 32767
1372 || ((long) inode_number - dir.inode_number) < -32768) {
1373 if (dir.entry_count_p >= 0) {
1374 squashfs_dir_header dir_header = new squashfs_dir_header();
1375
1376 if ((dir.buffp + squashfs_constants.SQUASHFS_DIR_ENTRY_SIZE
1377 + size - dir.index_count_p) > squashfs_constants.SQUASHFS_METADATA_SIZE) {
1378 if (dir.i_count % squashfs_constants.I_COUNT_SIZE == 0) {
1379 dir.index = realloc(
1380 dir.index,
1381 (dir.i_count + squashfs_constants.I_COUNT_SIZE)
1382 * squashfs_constants.CACHED_DIR_INDEX_SIZE);
1383 }
1384 dir.index[dir.i_count].index.index = dir.buffp;
1385 dir.index[dir.i_count].index.size = (int) (size - 1);
1386 dir.index[dir.i_count++].name = name;
1387 dir.i_size += squashfs_constants.SQUASHFS_DIR_INDEX_SIZE
1388 + size;
1389 dir.index_count_p = dir.buffp;
1390 }
1391
1392 dir_header.count = dir.entry_count - 1;
1393 dir_header.start_block = dir.start_block;
1394 dir_header.inode_number = dir.inode_number;
1395
1396 ByteArrayOutputStream zbuffer = new ByteArrayOutputStream();
1397 EasyIOOutputStream zbuffer_stream = new EasyIOOutputStream(
1398 zbuffer);
1399 zbuffer_stream.write(dir_header);
1400 zbuffer_stream.close();
1401
1402 RandomAccessByteArray buff_array = new RandomAccessByteArray(
1403 dir.buff);
1404 buff_array.seek(dir.entry_count_p);
1405 EasyIOOutputStream buff_out = new EasyIOOutputStream(
1406 new RandomAccessByteArrayOutputStreamAdapter(buff_array));
1407 buff_out.write(zbuffer.toByteArray());
1408
1409
1410
1411 }
1412
1413 dir.entry_count_p = dir.buffp;
1414 dir.start_block = start_block;
1415 dir.entry_count = 0;
1416 dir.inode_number = inode_number;
1417 dir.buffp += squashfs_constants.SQUASHFS_DIR_HEADER_SIZE;
1418 }
1419
1420 idir.offset = (int) offset;
1421 idir.type = type;
1422 idir.size = (int) (size - 1);
1423 idir.inode_number = (int) ((long) inode_number - dir.inode_number);
1424
1425 ByteArrayOutputStream zbuffer = new ByteArrayOutputStream();
1426 EasyIOOutputStream zbuffer_stream = new EasyIOOutputStream(zbuffer);
1427 zbuffer_stream.write(idir);
1428 zbuffer_stream.close();
1429
1430 RandomAccessByteArray buff_array = new RandomAccessByteArray(dir.buff);
1431 buff_array.seek(dir.buffp);
1432 buff_array.write(zbuffer.toByteArray());
1433
1434
1435
1436 buff_array.write(name.getBytes(), 0, (int) size);
1437 dir.buffp += squashfs_constants.SQUASHFS_DIR_ENTRY_SIZE + size;
1438 dir.entry_count++;
1439 }
1440
1441 /***
1442 * @param dir
1443 * @param dir_info
1444 * @return z
1445 * @throws IOException
1446 * @throws EasyIOException
1447 */
1448 private dir_ent scan2_readdir(directory dir, dir_info dir_info)
1449 throws EasyIOException, IOException {
1450 int current_count;
1451
1452 while ((current_count = (int) dir_info.current_count++) < dir_info.count) {
1453 if (dir_info.list.get(current_count).data != null) {
1454 add_dir(dir_info.list.get(current_count).data.inode,
1455 dir_info.list.get(current_count).data.inode_number,
1456 dir_info.list.get(current_count).name, dir_info.list
1457 .get(current_count).data.type, dir);
1458 } else {
1459 return dir_info.list.get(current_count);
1460 }
1461 }
1462 return null;
1463 }
1464
1465 /***
1466 * @param dir
1467 */
1468 private void scan2_init_dir(directory dir) {
1469 dir.buff = new byte[squashfs_constants.SQUASHFS_METADATA_SIZE];
1470
1471 dir.size = squashfs_constants.SQUASHFS_METADATA_SIZE;
1472 dir.buffp = dir.index_count_p = 0;
1473 dir.entry_count = 256;
1474 dir.entry_count_p = -1;
1475 dir.index = null;
1476 dir.i_count = dir.i_size = 0;
1477 }
1478
1479 /***
1480 * @param dir_ent
1481 * @param type
1482 * @param byte_size
1483 * @param start_block
1484 * @param offset
1485 * @param block_list
1486 * @param block_listp
1487 * @param fragment
1488 * @param dir_in
1489 * @return inode.
1490 * @throws IOException
1491 * @throws EasyIOException
1492 * @throws SquashFSException
1493 */
1494 private long create_inode(dir_ent dir_ent, int type, long byte_size,
1495 long start_block, long offset, long[] block_list, int block_listp,
1496 fragment fragment, directory dir_in) throws EasyIOException,
1497 IOException, SquashFSException {
1498 stat buf = dir_ent.inode.buf;
1499 squashfs_base_inode_header inode_header;
1500 int inode_number = (int) ((type == squashfs_constants.SQUASHFS_LDIR_TYPE || type == squashfs_constants.SQUASHFS_DIR_TYPE) ? dir_ent.inode.inode_number
1501 : dir_ent.inode.inode_number + this.dir_inode_no);
1502 int inode_offset;
1503 String filename = dir_ent.pathname;
1504 long nlink = dir_ent.inode.nlink;
1505
1506 if (type == squashfs_constants.SQUASHFS_FILE_TYPE) {
1507 inode_header = new squashfs_reg_inode_header();
1508 }
1509
1510 else if (type == squashfs_constants.SQUASHFS_DIR_TYPE) {
1511 inode_header = new squashfs_dir_inode_header();
1512 }
1513
1514 else if (type == squashfs_constants.SQUASHFS_SYMLINK_TYPE) {
1515 inode_header = new squashfs_symlink_inode_header();
1516 }
1517
1518 else {
1519 throw new SquashFSException("unknown type '" + type + "'");
1520 }
1521
1522 inode_header.mode = squashfs_constants.SQUASHFS_MODE(buf.st_mode);
1523 inode_header.uid = get_uid(this.global_uid == -1 ? buf.st_uid
1524 : this.global_uid);
1525 inode_header.inode_type = type;
1526 inode_header.guid = get_guid(this.global_uid == -1 ? buf.st_uid
1527 : this.global_uid, this.global_gid == -1 ? buf.st_gid
1528 : this.global_gid);
1529 inode_header.mtime = buf.st_mtime;
1530 inode_header.inode_number = inode_number;
1531
1532 if (type == squashfs_constants.SQUASHFS_FILE_TYPE) {
1533 int i;
1534
1535 inode_offset = get_inode(squashfs_constants.SQUASHFS_REG_INODE_HEADER_SIZE
1536 + offset * 4);
1537 ((squashfs_reg_inode_header) inode_header).file_size = byte_size;
1538 ((squashfs_reg_inode_header) inode_header).start_block = start_block;
1539 ((squashfs_reg_inode_header) inode_header).fragment = fragment.index;
1540 ((squashfs_reg_inode_header) inode_header).offset = fragment.offset;
1541
1542 ByteArrayOutputStream zbuffer = new ByteArrayOutputStream();
1543 EasyIOOutputStream zout = new EasyIOOutputStream(zbuffer);
1544 zout.write(inode_header);
1545 zout.close();
1546 RandomAccessByteArray inode_array = get_inode_array(inode_offset);
1547 EasyIOOutputStream inode_stream = new EasyIOOutputStream(
1548 new RandomAccessByteArrayOutputStreamAdapter(inode_array));
1549 byte[] zbuffer_array = zbuffer.toByteArray();
1550 inode_stream.write(zbuffer_array, 0, zbuffer_array.length);
1551 for (int z = 0; z < offset; z++) {
1552 inode_stream.writeUINT32(block_list[z]);
1553 }
1554
1555
1556
1557
1558 if (log.isTraceEnabled()) {
1559 log.trace("File inode, file_size " + ((int) byte_size)
1560 + ", start_block " + Long.toHexString(start_block)
1561 + ", blocks " + (offset) + ", fragment "
1562 + (fragment.index) + ", offset " + fragment.offset
1563 + ", size " + fragment.size);
1564 for (i = 0; i < offset; i++)
1565 log.trace("Block " + i + ", size " + block_list[i]);
1566 }
1567 }
1568
1569 else if (type == squashfs_constants.SQUASHFS_LREG_TYPE) {
1570 throw new RuntimeException("not implemented");
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594 }
1595
1596 else if (type == squashfs_constants.SQUASHFS_LDIR_TYPE) {
1597 throw new RuntimeException("not implemented");
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636 }
1637
1638 else if (type == squashfs_constants.SQUASHFS_DIR_TYPE) {
1639 inode_offset = get_inode(squashfs_constants.SQUASHFS_DIR_INODE_HEADER_SIZE);
1640 ((squashfs_dir_inode_header) inode_header).nlink = dir_ent.dir.directory_count + 2;
1641 ((squashfs_dir_inode_header) inode_header).file_size = byte_size;
1642 ((squashfs_dir_inode_header) inode_header).offset = offset;
1643 ((squashfs_dir_inode_header) inode_header).start_block = start_block;
1644 ((squashfs_dir_inode_header) inode_header).parent_inode = dir_ent.our_dir != null ? dir_ent.our_dir.dir_ent.inode.inode_number
1645 : this.dir_inode_no + this.inode_no;
1646
1647 ByteArrayOutputStream zbuffer = new ByteArrayOutputStream();
1648 EasyIOOutputStream zout = new EasyIOOutputStream(zbuffer);
1649 zout.write(inode_header);
1650 zout.close();
1651 RandomAccessByteArray inode_array = get_inode_array(inode_offset);
1652 byte[] zbuffer_array = zbuffer.toByteArray();
1653 inode_array.write(zbuffer_array, 0, zbuffer_array.length);
1654
1655
1656
1657 log.trace("Directory inode, file_size " + byte_size
1658 + ", start_block " + start_block + ", offset "
1659 + Long.toHexString(offset) + ", nlink "
1660 + (dir_ent.dir.directory_count + 2));
1661 }
1662
1663 else if (type == squashfs_constants.SQUASHFS_CHRDEV_TYPE
1664 || type == squashfs_constants.SQUASHFS_BLKDEV_TYPE) {
1665 throw new RuntimeException("not implemented");
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681 }
1682
1683 else if (type == squashfs_constants.SQUASHFS_SYMLINK_TYPE) {
1684 int b;
1685 byte[] buff = new byte[65536];
1686
1687 if ((b = readlink(filename, buff, 65536)) == -1) {
1688 throw new SquashFSException("Error in reading symbolic link");
1689 }
1690
1691 if (b == 65536) {
1692 throw new SquashFSException(
1693 "Symlink is greater than 65536 bytes!");
1694 }
1695
1696 inode_offset = get_inode(squashfs_constants.SQUASHFS_SYMLINK_INODE_HEADER_SIZE
1697 + b);
1698 ((squashfs_symlink_inode_header) inode_header).nlink = nlink;
1699 ((squashfs_symlink_inode_header) inode_header).symlink_size = b;
1700
1701 ByteArrayOutputStream zbuffer = new ByteArrayOutputStream();
1702 EasyIOOutputStream zout = new EasyIOOutputStream(zbuffer);
1703 zout.write(inode_header);
1704 zout.close();
1705 RandomAccessByteArray inode_array = get_inode_array(inode_offset);
1706 byte[] zbuffer_array = zbuffer.toByteArray();
1707 inode_array.write(zbuffer_array, 0, zbuffer_array.length);
1708
1709
1710
1711 inode_array.write(buff, 0, b);
1712 log.trace("Symbolic link inode, symlink_size " + b + ", nlink "
1713 + nlink);
1714 }
1715
1716 else if (type == squashfs_constants.SQUASHFS_FIFO_TYPE
1717 || type == squashfs_constants.SQUASHFS_SOCKET_TYPE) {
1718 throw new RuntimeException("not implemented");
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732 }
1733
1734 else {
1735 throw new SquashFSException("unknown type '" + type + "'");
1736 }
1737
1738 long i_no = MKINODE(inode_offset);
1739 this.inode_count++;
1740
1741 log.trace("Created inode 0x" + Long.toHexString(i_no) + ", type "
1742 + type + ", uid " + inode_header.uid + ", guid "
1743 + inode_header.guid);
1744
1745 return i_no;
1746 }
1747
1748 /***
1749 * @param filename the filename to find.
1750 * @param buff the buffer to populate with link.
1751 * @param i the max.
1752 * @return the number of bytes read.
1753 * @throws SquashFSException
1754 */
1755 private int readlink(String filename, byte[] buff, int i)
1756 throws SquashFSException {
1757 BaseFile bf = this.manifest.find(filename);
1758 if (bf == null) {
1759 throw new SquashFSException("could not find file '" + filename
1760 + "'");
1761 }
1762 if (bf instanceof SymLink) {
1763 String linkName = ((SymLink) bf).getLinkName();
1764 int cp = Math.min(i, linkName.length());
1765 System.arraycopy(linkName.getBytes(), 0, buff, 0, cp);
1766 return cp;
1767 }
1768 throw new SquashFSException("file is not a symbolic link");
1769 }
1770
1771 /***
1772 * @param inode_offset
1773 * @return inode.
1774 */
1775 private long MKINODE(int inode_offset) {
1776 return ((long) (((long) this.inode_bytes << 16) + inode_offset));
1777 }
1778
1779 /***
1780 * @param req_size
1781 * @return the offset into {@link SquashFSWriter#data_cache}.
1782 * @throws IOException
1783 * @throws EasyIOException
1784 */
1785 private int get_inode(long req_size) throws IOException, EasyIOException {
1786 int data_space;
1787 int c_byte;
1788
1789 RandomAccessByteArray inode_table_array = new RandomAccessByteArray(
1790 this.inode_table);
1791 EasyIOOutputStream inode_table_stream = new EasyIOOutputStream(
1792 new RandomAccessByteArrayOutputStreamAdapter(inode_table_array));
1793 RandomAccessByteArray data_cache_array = new RandomAccessByteArray(
1794 this.data_cache);
1795
1796 while (this.cache_bytes >= squashfs_constants.SQUASHFS_METADATA_SIZE) {
1797 if ((this.inode_size - this.inode_bytes) < ((squashfs_constants.SQUASHFS_METADATA_SIZE << 1)) + 2) {
1798 this.inode_table = realloc(this.inode_table, this.inode_size
1799 + (squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2);
1800 this.inode_size += (squashfs_constants.SQUASHFS_METADATA_SIZE << 1) + 2;
1801 }
1802
1803 inode_table_array.seek(this.inode_bytes + this.block_offset);
1804 c_byte = (int) mangle(inode_table_stream, data_cache_array,
1805 squashfs_constants.SQUASHFS_METADATA_SIZE,
1806 squashfs_constants.SQUASHFS_METADATA_SIZE, this.noI, 0);
1807 log.trace("Inode block @ " + Long.toHexString(this.inode_bytes)
1808 + ", size " + c_byte);
1809
1810 inode_table_array.seek(this.inode_bytes);
1811 inode_table_stream.writeUINT16(c_byte);
1812
1813
1814
1815 if (this.check_data) {
1816 this.inode_table[this.inode_bytes + this.block_offset - 1] = squashfs_constants.SQUASHFS_MARKER_BYTE;
1817 }
1818 this.inode_bytes += squashfs_constants
1819 .SQUASHFS_COMPRESSED_SIZE(c_byte)
1820 + this.block_offset;
1821 this.total_inode_bytes += squashfs_constants.SQUASHFS_METADATA_SIZE
1822 + this.block_offset;
1823 data_cache_array.seek(0);
1824 data_cache_array.write(this.data_cache,
1825 squashfs_constants.SQUASHFS_METADATA_SIZE, this.cache_bytes
1826 - squashfs_constants.SQUASHFS_METADATA_SIZE);
1827 this.cache_bytes -= squashfs_constants.SQUASHFS_METADATA_SIZE;
1828 }
1829
1830 data_space = (int) (this.cache_size - this.cache_bytes);
1831 if (data_space < req_size) {
1832 int realloc_size = (int) (this.cache_size == 0 ? ((req_size + squashfs_constants.SQUASHFS_METADATA_SIZE) & ~(squashfs_constants.SQUASHFS_METADATA_SIZE - 1))
1833 : req_size - data_space);
1834
1835 this.data_cache = realloc(this.data_cache,
1836 (int) (this.cache_size + realloc_size));
1837 this.cache_size += realloc_size;
1838 }
1839
1840 this.cache_bytes += req_size;
1841
1842 return (int) (this.cache_bytes - req_size);
1843 }
1844
1845 /***
1846 * @param offset
1847 * @return the array.
1848 */
1849 private RandomAccessByteArray get_inode_array(int offset) {
1850 RandomAccessByteArray array = new RandomAccessByteArray(this.data_cache);
1851 array.seek(offset);
1852 return array;
1853 }
1854
1855 /***
1856 * @param uid
1857 * @param guid
1858 * @return z.
1859 */
1860 private int get_guid(long uid, long guid) {
1861 int i;
1862
1863 if (uid == guid) {
1864 return squashfs_constants.SQUASHFS_GUIDS;
1865 }
1866
1867 for (i = 0; (i < this.guid_count) && this.guids[i] != guid; i++) {
1868
1869 }
1870 if (i == this.guid_count) {
1871 if (this.guid_count == squashfs_constants.SQUASHFS_GUIDS) {
1872 log
1873 .error("Out of gids! - using gid 0 - probably not what's wanted!");
1874 return squashfs_constants.SQUASHFS_GUIDS;
1875 } else {
1876 this.guids[this.guid_count++] = guid;
1877 }
1878 }
1879
1880 return i;
1881 }
1882
1883 /***
1884 * @param uid
1885 * @return z.
1886 */
1887 private int get_uid(long uid) {
1888 int i;
1889
1890 for (i = 0; (i < this.uid_count) && this.uids[i] != uid; i++) {
1891
1892 }
1893 if (i == this.uid_count) {
1894 if (this.uid_count == squashfs_constants.SQUASHFS_UIDS) {
1895 log
1896 .error("Out of uids! - using uid 0 - probably not what's wanted!");
1897 i = 0;
1898 } else {
1899 this.uids[this.uid_count++] = uid;
1900 }
1901 }
1902
1903 return i;
1904 }
1905
1906 /***
1907 * @param dir_ent
1908 * @param size
1909 * @param duplicate_file
1910 * @return z.
1911 * @throws SquashFSException
1912 * @throws IOException
1913 * @throws EasyIOException
1914 */
1915 private long write_file(dir_ent dir_ent, long size, boolean[] duplicate_file)
1916 throws SquashFSException, IOException, EasyIOException {
1917 long read_size = (size > squashfs_constants.SQUASHFS_MAX_FILE_SIZE) ? squashfs_constants.SQUASHFS_MAX_FILE_SIZE
1918 : size;
1919 int blocks = (int) ((read_size + this.superBlock.block_size - 1) >> this.superBlock.block_log);
1920 int allocated_blocks = blocks;
1921 long[] block_list;
1922 int block_listp;
1923 long frag_bytes;
1924 String filename = dir_ent.pathname;
1925 int whole_file = 1;
1926 byte[] c_buffer;
1927 long start;
1928 int block = 0;
1929 int i;
1930 long file_bytes = 0;
1931 long bbytes;
1932 byte[] buff = new byte[(int) this.superBlock.block_size];
1933 long c_byte;
1934 duplicate_buffer_handle handle = new duplicate_buffer_handle();
1935 file_info dupl_ptr = null;
1936 fragment fragment = new fragment();
1937
1938 RandomAccessByteArray buff_array = new RandomAccessByteArray(buff);
1939
1940 block_list = new long[blocks];
1941 block_listp = 0;
1942
1943 if (!this.no_fragments
1944 && (read_size < this.superBlock.block_size || this.always_use_fragments)) {
1945 allocated_blocks = blocks = (int) (read_size >> this.superBlock.block_log);
1946 frag_bytes = read_size % this.superBlock.block_size;
1947 } else {
1948 frag_bytes = 0;
1949 }
1950
1951 if (size > read_size) {
1952 throw new SquashFSException("file " + filename + " truncated to "
1953 + squashfs_constants.SQUASHFS_MAX_FILE_SIZE + " bytes\n");
1954 }
1955
1956 this.total_bytes += read_size;
1957 BaseFile bf = this.manifest.find(filename);
1958 IRandomAccessSource randFile = this.dataProvider.getData(this.manifest,
1959 bf);
1960
1961 {
1962 long bytes_var = (((long) allocated_blocks) + 1) << this.superBlock.block_log;
1963 c_buffer = new byte[(int) bytes_var];
1964 }
1965
1966 RandomAccessByteArray c_buffer_array = new RandomAccessByteArray(
1967 c_buffer);
1968 EasyIOOutputStream c_buffer_stream = new EasyIOOutputStream(
1969 new RandomAccessByteArrayOutputStreamAdapter(c_buffer_array));
1970
1971 for (start = this.bytes; block < blocks; file_bytes += bbytes) {
1972 for (i = 0, bbytes = 0; (i < allocated_blocks) && (block < blocks); i++) {
1973 int available_bytes = (int) (read_size
1974 - (block * this.superBlock.block_size) > this.superBlock.block_size ? this.superBlock.block_size
1975 : read_size - (block * this.superBlock.block_size));
1976 randFile.read(buff, 0, available_bytes);
1977 c_buffer_array.seek((int) bbytes);
1978 buff_array.seek(0);
1979 c_byte = mangle(c_buffer_stream, buff_array, available_bytes,
1980 (int) this.superBlock.block_size, this.noD, 1);
1981 block_list[block++] = c_byte;
1982 bbytes += squashfs_constants
1983 .SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
1984 }
1985 if (whole_file == 0) {
1986 this.dest.seek(this.bytes);
1987 this.dest.write(c_buffer, 0, (int) bbytes);
1988 this.bytes += bbytes;
1989 }
1990 }
1991
1992 if (frag_bytes != 0) {
1993 randFile.read(buff, 0, (int) frag_bytes);
1994 }
1995
1996 if (whole_file != 0) {
1997 handle.ptr = 0;
1998 handle.ptrdata = c_buffer;
1999 int[] zzblock_listp = new int[] { block_listp };
2000 long[] zzstart = new long[] { start };
2001 fragment[] zzfragment = new fragment[] { fragment };
2002 if (this.duplicate_checking
2003 && (dupl_ptr = duplicate(new read_from_buffer(), handle,
2004 file_bytes, zzblock_listp, zzstart, blocks,
2005 zzfragment, buff, frag_bytes)) == null) {
2006 duplicate_file[0] = true;
2007 } else {
2008 this.dest.seek(this.bytes);
2009 this.dest.write(c_buffer, 0, (int) file_bytes);
2010 this.bytes += file_bytes;
2011 }
2012 } else {
2013 handle.start = start;
2014 int[] zzblock_listp = new int[] { block_listp };
2015 long[] zzstart = new long[] { start };
2016 fragment[] zzfragment = new fragment[] { fragment };
2017 if (this.duplicate_checking
2018 && (dupl_ptr = duplicate(new read_from_file(), handle,
2019 file_bytes, zzblock_listp, zzstart, blocks,
2020 zzfragment, buff, frag_bytes)) == null) {
2021 start = zzstart[0];
2022 this.bytes = start;
2023 if (this.block_device == 0) {
2024 this.dest.setLength(this.bytes);
2025 }
2026 duplicate_file[0] = true;
2027 }
2028 }
2029
2030 if (!duplicate_file[0]) {
2031 fragment = get_and_fill_fragment(buff, frag_bytes);
2032 if (this.duplicate_checking) {
2033 dupl_ptr.fragment = fragment;
2034 }
2035
2036 duplicate_file[0] = false;
2037 }
2038
2039 this.file_count++;
2040 if (dir_ent.inode.nlink == 1 && read_size < ((long) (1 << 30) - 1)) {
2041 return create_inode(dir_ent, squashfs_constants.SQUASHFS_FILE_TYPE,
2042 read_size, start, (long) blocks, block_list, block_listp,
2043 fragment, null);
2044 } else {
2045 return create_inode(dir_ent, squashfs_constants.SQUASHFS_LREG_TYPE,
2046 read_size, start, (long) blocks, block_list, block_listp,
2047 fragment, null);
2048 }
2049 }
2050
2051 /***
2052 * @param get_next_file_block
2053 * @param file_start
2054 * @param bytesarg
2055 * @param block_list
2056 * @param start
2057 * @param blocks
2058 * @param fragment
2059 * @param frag_data
2060 * @param frag_bytes
2061 * @return z.
2062 * @throws IOException
2063 */
2064 private file_info duplicate(IReadFrom get_next_file_block,
2065 duplicate_buffer_handle file_start, long bytesarg,
2066 int[] block_list, long[] start, int blocks, fragment[] fragment,
2067 byte[] frag_data, long frag_bytes) throws IOException {
2068 int checksum = get_checksum(get_next_file_block, file_start, bytesarg);
2069 duplicate_buffer_handle handle = new duplicate_buffer_handle();
2070 handle.ptrdata = frag_data;
2071 int fragment_checksum = get_checksum(new read_from_buffer(), handle,
2072 frag_bytes);
2073 file_info dupl_ptr;
2074
2075 dupl_ptr = bytesarg != 0 ? this.dupl[checksum]
2076 : this.frag_dups[fragment_checksum];
2077
2078 for (; dupl_ptr != null; dupl_ptr = dupl_ptr.next)
2079 if (bytesarg == dupl_ptr.bytes
2080 && frag_bytes == dupl_ptr.fragment.size
2081 && fragment_checksum == dupl_ptr.fragment_checksum) {
2082 byte[] buffer1 = new byte[(int) squashfs_constants.SQUASHFS_FILE_MAX_SIZE];
2083 long dup_bytes = dupl_ptr.bytes;
2084 long dup_start = dupl_ptr.start;
2085 duplicate_buffer_handle position = file_start;
2086 byte[] buffer;
2087 while (dup_bytes != 0) {
2088 int avail_bytes = (int) (dup_bytes > squashfs_constants.SQUASHFS_FILE_MAX_SIZE ? squashfs_constants.SQUASHFS_FILE_MAX_SIZE
2089 : dup_bytes);
2090
2091 buffer = get_next_file_block.read_from(position,
2092 avail_bytes);
2093 this.dest.seek(dup_start);
2094 this.dest.read(buffer1, 0, avail_bytes);
2095 if (memcmp(buffer, buffer1, avail_bytes) != 0) {
2096 break;
2097 }
2098 dup_bytes -= avail_bytes;
2099 dup_start += avail_bytes;
2100 }
2101 if (dup_bytes == 0) {
2102 byte[] fragment_buffer1;
2103
2104 if (dupl_ptr.fragment.index == this.superBlock.fragments
2105 || dupl_ptr.fragment.index == squashfs_constants.SQUASHFS_INVALID_FRAG) {
2106 fragment_buffer1 = copyArrayFromOffset(
2107 this.fragment_data, dupl_ptr.fragment.offset);
2108 }
2109
2110 else if (dupl_ptr.fragment.index == this.cached_frag1) {
2111 fragment_buffer1 = copyArrayFromOffset(
2112 this.cached_fragment, dupl_ptr.fragment.offset);
2113 }
2114
2115 else {
2116 fragment_buffer1 = get_fragment(this.cached_fragment,
2117 dupl_ptr.fragment);
2118 this.cached_frag1 = dupl_ptr.fragment.index;
2119 }
2120
2121 if (frag_bytes == 0
2122 || memcmp(frag_data, fragment_buffer1,
2123 (int) frag_bytes) == 0) {
2124 log.trace("Found duplicate file, start "
2125 + Long.toHexString(dupl_ptr.start) + ", size "
2126 + dupl_ptr.bytes + ", checksum 0x"
2127 + Long.toHexString(dupl_ptr.checksum)
2128 + ", fragment " + dupl_ptr.fragment.index
2129 + ", size " + frag_bytes + ", offset "
2130 + dupl_ptr.fragment.offset + ", checksum 0x"
2131 + Long.toHexString(fragment_checksum));
2132 block_list[0] = dupl_ptr.block_list;
2133 start[0] = dupl_ptr.start;
2134 fragment[0] = dupl_ptr.fragment;
2135 return null;
2136 }
2137 }
2138 }
2139
2140 dupl_ptr = new file_info();
2141
2142 dupl_ptr.bytes = bytesarg;
2143 dupl_ptr.checksum = checksum;
2144 dupl_ptr.start = start[0];
2145 dupl_ptr.fragment_checksum = fragment_checksum;
2146 dupl_ptr.block_list = block_list[0];
2147
2148 this.dup_files++;
2149 if (bytesarg != 0) {
2150 dupl_ptr.next = this.dupl[checksum];
2151 this.dupl[checksum] = dupl_ptr;
2152 } else {
2153 dupl_ptr.next = this.frag_dups[fragment_checksum];
2154 this.frag_dups[fragment_checksum] = dupl_ptr;
2155 }
2156
2157 return dupl_ptr;
2158 }
2159
2160 /***
2161 * @param buffer
2162 * @param fragment
2163 * @return z.
2164 * @throws IOException
2165 */
2166 private byte[] get_fragment(byte[] buffer, fragment fragment)
2167 throws IOException {
2168 squashfs_fragment_entry disk_fragment = this.fragment_table[fragment.index];
2169 int size = (int) squashfs_constants
2170 .SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment.size);
2171
2172 if (squashfs_constants.SQUASHFS_COMPRESSED_BLOCK(disk_fragment.size)) {
2173 byte[] cbuffer = new byte[(int) this.superBlock.block_size];
2174
2175 this.dest.seek(disk_fragment.start_block);
2176 this.dest.read(cbuffer, 0, size);
2177
2178 InflaterInputStream in = new InflaterInputStream(
2179 new ByteArrayInputStream(cbuffer));
2180 in.read(buffer);
2181 in.close();
2182 } else {
2183 this.dest.seek(disk_fragment.start_block);
2184 this.dest.read(buffer, 0, size);
2185 }
2186
2187 return copyArrayFromOffset(buffer, fragment.offset);
2188 }
2189
2190 /***
2191 * @param buffer
2192 * @param offset
2193 * @return new array.
2194 */
2195 private byte[] copyArrayFromOffset(byte[] buffer, int offset) {
2196 byte[] result = new byte[buffer.length - offset];
2197 System.arraycopy(buffer, offset, result, 0, result.length);
2198 return result;
2199 }
2200
2201 /***
2202 * @param buffer
2203 * @param buffer1
2204 * @param avail_bytes
2205 * @return z.
2206 */
2207 private int memcmp(byte[] buffer, byte[] buffer1, int avail_bytes) {
2208 for (int i = 0; i < avail_bytes; i++) {
2209 if (buffer[i] < buffer1[i]) {
2210 return -1;
2211 }
2212 if (buffer[i] > buffer1[i]) {
2213 return 1;
2214 }
2215 }
2216 return 0;
2217 }
2218
2219 /***
2220 * @param get_next_file_block
2221 * @param handle
2222 * @param l
2223 * @return the checksum.
2224 * @throws IOException
2225 */
2226 private int get_checksum(IReadFrom get_next_file_block,
2227 duplicate_buffer_handle handle, long l) throws IOException {
2228 int chksum = 0;
2229 long bytes_var = 0;
2230 byte[] b;
2231 int bp;
2232 duplicate_buffer_handle position = handle;
2233
2234 while (l != 0) {
2235 bytes_var = l > squashfs_constants.SQUASHFS_FILE_MAX_SIZE ? squashfs_constants.SQUASHFS_FILE_MAX_SIZE
2236 : l;
2237 l -= bytes_var;
2238 b = get_next_file_block.read_from(position, bytes_var);
2239 bp = 0;
2240 while (bytes_var-- != 0) {
2241 chksum = (chksum & 1) != 0 ? (chksum >> 1) | 0x8000
2242 : chksum >> 1;
2243 chksum += b[bp++];
2244 }
2245 }
2246
2247 return chksum;
2248 }
2249
2250 /***
2251 * @param buff
2252 * @param size
2253 * @return z.
2254 * @throws IOException
2255 */
2256 private fragment get_and_fill_fragment(byte[] buff, long size)
2257 throws IOException {
2258 fragment ffrg;
2259
2260 if (size == 0)
2261 return empty_fragment;
2262
2263 if (this.fragment_size + size > this.superBlock.block_size) {
2264 write_fragment();
2265 }
2266
2267 ffrg = new fragment();
2268
2269 ffrg.index = (int) this.superBlock.fragments;
2270 ffrg.offset = this.fragment_size;
2271 ffrg.size = size;
2272 RandomAccessByteArray array = new RandomAccessByteArray(
2273 this.fragment_data);
2274 array.seek(this.fragment_size);
2275 array.write(buff, 0, (int) size);
2276 this.fragment_size += size;
2277
2278 return ffrg;
2279 }
2280
2281 /***
2282 * @param path
2283 * @param destDir
2284 * @return the dir info.
2285 * @throws IOException
2286 */
2287 private dir_info dir_scan1(Directory path, String destDir)
2288 throws IOException {
2289 dir_info dir, sub_dir;
2290 stat buf;
2291 String filename;
2292 String dir_name;
2293
2294 if ((dir = scan1_opendir(path)) == null) {
2295 log.error("Could not open " + path.getName() + ", skipping...");
2296 return dir;
2297 }
2298
2299 for (BaseFile f : path.getSubentries()) {
2300 filename = destDir + "/" + f.getName();
2301 dir_name = f.getName();
2302 buf = fromBaseFile(f);
2303
2304
2305
2306
2307
2308
2309 if (f instanceof Directory) {
2310 if ((sub_dir = dir_scan1((Directory) f, filename)) == null)
2311 continue;
2312 dir.directory_count++;
2313 } else
2314 sub_dir = null;
2315
2316 add_dir_entry(dir_name, filename, sub_dir, lookup_inode(buf), null,
2317 dir);
2318 }
2319
2320 scan1_freedir(dir);
2321 sort_directory(dir);
2322
2323 return dir;
2324 }
2325
2326 /***
2327 * @param name
2328 * @param pathname
2329 * @param sub_dir
2330 * @param inode_info_param
2331 * @param data
2332 * @param dir
2333 */
2334 private void add_dir_entry(String name, String pathname, dir_info sub_dir,
2335 inode_info inode_info_param, Object data, dir_info dir) {
2336
2337 dir_ent dirent = new dir_ent();
2338 if (dir.list == null) {
2339 dir.list = new ArrayList<dir_ent>();
2340 }
2341 dir.list.add(dirent);
2342
2343 if (sub_dir != null)
2344 sub_dir.dir_ent = dirent;
2345 dirent.name = name;
2346 dirent.pathname = pathname != null ? pathname : null;
2347 dirent.inode = inode_info_param;
2348 dirent.dir = sub_dir;
2349 dirent.our_dir = dir;
2350 dirent.data = (old_root_entry_info) data;
2351 dir.count++;
2352 dir.byte_count += pathname.length()
2353 + squashfs_constants.SQUASHFS_DIR_ENTRY_SIZE;
2354 }
2355
2356 /***
2357 * @param buf
2358 * @return the inode info.
2359 */
2360 private inode_info lookup_inode(stat buf) {
2361 inode_info inode, inodefound;
2362
2363 inodefound = inode = this.inode_info.get(buf.st_dev + "" + buf.st_ino);
2364
2365 while (inode != null) {
2366 if (buf.equals(inode.buf)) {
2367 inode.nlink++;
2368 return inode;
2369 }
2370 inode = inode.next;
2371 }
2372
2373 inode = new inode_info();
2374
2375 try {
2376 if (buf == null) {
2377 inode.buf = null;
2378 } else {
2379 inode.buf = (stat) buf.clone();
2380 }
2381 } catch (CloneNotSupportedException e) {
2382 throw new RuntimeException(e);
2383 }
2384
2385 inode.inode = squashfs_constants.SQUASHFS_INVALID_BLK;
2386 inode.nlink = 1;
2387 if ((buf.st_mode & stat.S_IFMT) == stat.S_IFDIR) {
2388 inode.inode_number = this.dir_inode_no++;
2389 } else {
2390 inode.inode_number = this.inode_no++;
2391 }
2392
2393 inode.next = inodefound;
2394 this.inode_info.put(buf.st_dev + "" + buf.st_ino, inode);
2395
2396 return inode;
2397 }
2398
2399 /***
2400 * @param dir
2401 */
2402 private void sort_directory(dir_info dir) {
2403 if (dir.list == null) {
2404 dir.list = new ArrayList<dir_ent>();
2405 }
2406 Collections.sort(dir.list, new Comparator<dir_ent>() {
2407
2408 public int compare(dir_ent o1, dir_ent o2) {
2409 if (o1.name == null || o2.name == null) {
2410 return 0;
2411 }
2412 return o1.name.compareTo(o2.name);
2413 }
2414 });
2415
2416 if ((dir.count < 257 && dir.byte_count < squashfs_constants.SQUASHFS_METADATA_SIZE)) {
2417 dir.dir_is_ldir = false;
2418 }
2419 }
2420
2421 /***
2422 * @param dir
2423 */
2424 private void scan1_freedir(dir_info dir) {
2425
2426 }
2427
2428 /***
2429 * @param path
2430 * @return the open dir.
2431 */
2432 private dir_info scan1_opendir(Directory path) {
2433 dir_info dir;
2434
2435 dir = new dir_info();
2436
2437 if (path == null || path.getName() == null) {
2438 dir.pathname = ".";
2439 } else {
2440 dir.pathname = "./" + this.manifest.getPath(path);
2441 }
2442 dir.count = dir.directory_count = dir.current_count = dir.byte_count = 0;
2443 dir.dir_is_ldir = true;
2444 dir.list = null;
2445
2446 return dir;
2447 }
2448
2449 /***
2450 *
2451 */
2452 private interface IReadFrom {
2453 /***
2454 * @param handle
2455 * @param avail_bytes
2456 * @return data.
2457 * @throws IOException
2458 */
2459 byte[] read_from(duplicate_buffer_handle handle, long avail_bytes)
2460 throws IOException;
2461 }
2462
2463 /***
2464 *
2465 */
2466 private class read_from_buffer implements IReadFrom {
2467
2468 /***
2469 * {@inheritDoc}
2470 */
2471 public byte[] read_from(duplicate_buffer_handle handle, long avail_bytes) {
2472 byte[] v = new byte[handle.ptrdata.length];
2473 System.arraycopy(handle.ptrdata, handle.ptr, v, 0, Math.min(
2474 v.length, handle.ptrdata.length - handle.ptr));
2475 handle.ptr += avail_bytes;
2476 return v;
2477 }
2478 }
2479
2480 /***
2481 *
2482 */
2483 private class read_from_file implements IReadFrom {
2484
2485 /***
2486 *
2487 */
2488 byte[] read_from_file_buffer = new byte[(int) squashfs_constants.SQUASHFS_FILE_MAX_SIZE];
2489
2490 /***
2491 * {@inheritDoc}
2492 * @throws IOException
2493 */
2494 public byte[] read_from(duplicate_buffer_handle handle, long avail_bytes)
2495 throws IOException {
2496 SquashFSWriter.this.dest.seek(handle.start);
2497 SquashFSWriter.this.dest.read(this.read_from_file_buffer, 0,
2498 (int) avail_bytes);
2499 handle.start += avail_bytes;
2500 return this.read_from_file_buffer;
2501 }
2502 }
2503
2504 /***
2505 * @param bf
2506 * @return the stat
2507 * @throws IOException
2508 */
2509 private stat fromBaseFile(BaseFile bf) throws IOException {
2510 stat result = new stat();
2511 result.st_mode = bf.getMode();
2512 result.st_uid = bf.getUid();
2513 result.st_gid = bf.getGuid();
2514 result.st_mtime = bf.getMTime();
2515 result.st_dev = this.st_dev;
2516 result.st_ino = this.dataProvider.getIno(this.manifest, bf);
2517 result.st_size = this.dataProvider.getLength(this.manifest, bf);
2518 return result;
2519 }
2520
2521 /***
2522 * @param system the system to set
2523 */
2524 public static void setSystem(SquashFSSystem system) {
2525 SquashFSWriter.system = system;
2526 }
2527
2528 }