View Javadoc
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     //private int dummy_uid = 0;
300     /***
301      * 
302      */
303     //private int dummy_gid = 0;
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             /* if(!swap) { */
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              } else {
490              squashfs_uid uids_copy[squashfs.uid_count];
491 
492              SQUASHFS_SWAP_DATA(uids, uids_copy, squashfs.uid_count, sizeof(squashfs_uid) * 8);
493              write_bytes(fd, bytes, squashfs.uid_count * sizeof(squashfs_uid), (char *) uids_copy);
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             /* if(!swap) { */
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              } else {
510              squashfs_uid guids_copy[squashfs.guid_count];
511 
512              SQUASHFS_SWAP_DATA(guids, guids_copy, guid_count, sizeof(squashfs_uid) * 8);
513              write_bytes(fd, bytes, squashfs.guid_count * sizeof(squashfs_uid), (char *) guids_copy);
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          if(!swap) {
529          */
530         this.dest.seek(squashfs_constants.SQUASHFS_START);
531         this.out.write(this.superBlock);
532         /*
533          } else {
534          squashfs_super_block sBlk_copy;
535 
536          SQUASHFS_SWAP_SUPER_BLOCK((&sBlk), &sBlk_copy); 
537          write_bytes(fd, SQUASHFS_START, sizeof(squashfs_super_block), (char *) &sBlk_copy);
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         // TODO get other user names.
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         // TODO get other group names.
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          squashfs_fragment_entry *p = (squashfs_fragment_entry *) buffer;
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             /* if(!swap) { */
683             buffer_rand.seek(p);
684             buffer_out.write(this.fragment_table[i]);
685             /* } else {
686              SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_table[i], p);
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             /*if(!swap) {*/
698             cbuffer_rand.seek(0);
699             cbuffer_out.writeUINT16(c_byte);
700             /*} else {
701              SQUASHFS_SWAP_SHORTS((&c_byte), cbuffer, 1);
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         /* if(!swap) { */
716         this.dest.seek(this.bytes);
717         for (int i = 0; i < list.length; i++) {
718             this.out.writeINT64(list[i]);
719         }
720         /* } else {
721          squashfs_fragment_index slist[meta_blocks];
722          SQUASHFS_SWAP_FRAGMENT_INDEXES(list, slist, meta_blocks);
723          write_bytes(fd, bytes, sizeof(list), (char *) slist);
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             /* if(!swap) { */
806             directory_table_array.seek(this.directory_bytes);
807             directory_table_stream.writeUINT16(c_byte);
808             /* } else {
809              SQUASHFS_SWAP_SHORTS((&c_byte), (directory_table + directory_bytes), 1);
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             /* if(!swap) { */
902             inode_table_array.seek(this.inode_bytes);
903             inode_table_stream.writeUINT16(c_byte);
904             /* } else {
905              SQUASHFS_SWAP_SHORTS((&c_byte), (inode_table + inode_bytes), 1);
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          if (dir.getName() == null || dir.getName().length() == 0) {
1002          // dummy top level directory, if multiple sources specified on command line
1003          inode_info_var.buf.st_mode = stat.S_IRWXU | stat.S_IRWXG
1004          | stat.S_IRWXO;
1005          inode_info_var.buf.st_uid = this.dummy_uid;
1006          inode_info_var.buf.st_gid = this.dummy_gid;
1007          inode_info_var.buf.st_mtime = SquashFSUtils.getMTimeFromDate(system
1008          .getCurrentDate());
1009          } else {
1010          */
1011         inode_info_var.buf = fromBaseFile(dir);
1012         /*
1013          }
1014          */
1015 
1016         /*
1017          if(sorted)
1018          sort_files_and_write(dir_info);
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                     /* if(!sorted) { */
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         // do nothing.
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             /* if(!swap) { */
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             /* } else {
1204              SQUASHFS_SWAP_DIR_HEADER((&dir_header), (squashfs_dir_header *) dir->entry_count_p);
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             /*if(!swap) {*/
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             /*} else { 
1261              SQUASHFS_SWAP_SHORTS((&c_byte), (directory_table + directory_bytes), 1);
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                 /* if(!swap) { */
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                 /* } else {
1409                  SQUASHFS_SWAP_DIR_HEADER((&dir_header), (squashfs_dir_header *) dir->entry_count_p);
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         /* if(!swap) { */
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         /* } else {
1434          SQUASHFS_SWAP_DIR_ENTRY((&idir), idirp);
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             /*if(!swap) {*/
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             /*} else {
1555              SQUASHFS_SWAP_REG_INODE_HEADER(reg, inodep);
1556              SQUASHFS_SWAP_INTS(block_list, inodep->block_list, offset);
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              int i;
1573              squashfs_lreg_inode_header *reg = &inode_header.lreg, *inodep;
1574 
1575              inode = get_inode(sizeof(*reg) + offset * sizeof(unsigned int));
1576              inodep = (squashfs_lreg_inode_header *) inode;
1577              reg->nlink = nlink;
1578              reg->file_size = byte_size;
1579              reg->start_block = start_block;
1580              reg->fragment = fragment->index;
1581              reg->offset = fragment->offset;
1582              if(!swap) {
1583              memcpy(inodep, reg, sizeof(*reg));
1584              memcpy(inodep->block_list, block_list, offset * sizeof(unsigned int));
1585              } else {
1586              SQUASHFS_SWAP_LREG_INODE_HEADER(reg, inodep);
1587              SQUASHFS_SWAP_INTS(block_list, inodep->block_list, offset);
1588              }
1589              TRACE("Long file inode, file_size %lld, start_block %llx, blocks %d, fragment %d, offset %d, size %d, nlink %d\n", byte_size,
1590              start_block, offset, fragment->index, fragment->offset, fragment->size, nlink);
1591              for(i = 0; i < offset; i++)
1592              TRACE("Block %d, size %d\n", i, block_list[i]);
1593              */
1594         }
1595 
1596         else if (type == squashfs_constants.SQUASHFS_LDIR_TYPE) {
1597             throw new RuntimeException("not implemented");
1598 
1599             /* 
1600              int i;
1601              unsigned char *p;
1602              squashfs_ldir_inode_header *dir = &inode_header.ldir, *inodep;
1603              struct cached_dir_index *index = dir_in->index;
1604              unsigned int i_count = dir_in->i_count;
1605              unsigned int i_size = dir_in->i_size;
1606 
1607              if(byte_size >= 1 << 27)
1608              BAD_ERROR("directory greater than 2^27-1 bytes!\n");
1609 
1610              inode = get_inode(sizeof(*dir) + i_size);
1611              inodep = (squashfs_ldir_inode_header *) inode;
1612              dir->inode_type = SQUASHFS_LDIR_TYPE;
1613              dir->nlink = dir_ent->dir->directory_count + 2;
1614              dir->file_size = byte_size;
1615              dir->offset = offset;
1616              dir->start_block = start_block;
1617              dir->i_count = i_count;
1618              dir->parent_inode = dir_ent->our_dir ? dir_ent->our_dir->dir_ent->inode->inode_number : dir_inode_no + inode_no;
1619 
1620              if(!swap)
1621              memcpy(inode, dir, sizeof(*dir));
1622              else
1623              SQUASHFS_SWAP_LDIR_INODE_HEADER(dir, inode);
1624              p = (unsigned char *) inodep->index;
1625              for(i = 0; i < i_count; i++) {
1626              if(!swap)
1627              memcpy(p, &index[i].index, sizeof(squashfs_dir_index));
1628              else
1629              SQUASHFS_SWAP_DIR_INDEX(&index[i].index, p);
1630              memcpy(((squashfs_dir_index *)p)->name, index[i].name, index[i].index.size + 1);
1631              p += sizeof(squashfs_dir_index) + index[i].index.size + 1;
1632              }
1633              TRACE("Long directory inode, file_size %d, start_block %llx, offset %x, nlink %d\n", (int) byte_size,
1634              start_block, offset, dir_ent->dir->directory_count + 2);
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             /* if(!swap) { */
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             /*} else {
1655              SQUASHFS_SWAP_DIR_INODE_HEADER(dir, inode);
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              squashfs_dev_inode_header *dev = &inode_header.dev;
1670 
1671              inode = get_inode(sizeof(*dev));
1672              dev->nlink = nlink;
1673              dev->rdev = (unsigned short) ((major(buf->st_rdev) << 8) |
1674              (minor(buf->st_rdev) & 0xff));
1675              if(!swap)
1676              memcpy(inode, dev, sizeof(*dev));
1677              else
1678              SQUASHFS_SWAP_DEV_INODE_HEADER(dev, inode);
1679              TRACE("Device inode, rdev %x, nlink %d\n", dev->rdev, nlink);
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             /* if(!swap) { */
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             /* } else {
1709              SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode);
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              squashfs_ipc_inode_header *ipc = &inode_header.ipc;
1723 
1724              inode = get_inode(sizeof(*ipc));
1725              ipc->nlink = nlink;
1726              if(!swap)
1727              memcpy(inode, ipc, sizeof(*ipc));
1728              else
1729              SQUASHFS_SWAP_IPC_INODE_HEADER(ipc, inode);
1730              TRACE("ipc inode, type %s, nlink %d\n", type == SQUASHFS_FIFO_TYPE ? "fifo" : "socket", nlink);
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             /* if(!swap) { */
1810             inode_table_array.seek(this.inode_bytes);
1811             inode_table_stream.writeUINT16(c_byte);
1812             /* } else {
1813              SQUASHFS_SWAP_SHORTS((&c_byte), (inode_table + inode_bytes), 1);
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             // empty
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             // empty.
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              if(excluded(filename, &buf))
2306              continue;
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         // do nothing.
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 }