+ memcpy(buffer->data, compressed_buffer->data, size);
+ else
+@@ -1733,9 +1677,7 @@
+ entry->buffer->block = bytes;
+ bytes += compressed_size;
+ fragments_outstanding --;
+- pthread_mutex_unlock(&fragment_mutex);
+ queue_put(to_writer, entry->buffer);
+- pthread_mutex_lock(&fragment_mutex);
+ TRACE("fragment_locked writing fragment %d, compressed size %d"
+ "\n", entry->fragment, compressed_size);
+ free(entry);
+@@ -1758,6 +1700,8 @@
+ pthread_mutex_lock(&fragment_mutex);
+ insert_fragment_list(&frag_locked_list, entry);
+ pthread_mutex_unlock(&fragment_mutex);
++
++ return TRUE;
+ }
+
+
+@@ -1824,7 +1768,9 @@
+ unsigned short c_byte;
+ char cbuffer[(SQUASHFS_METADATA_SIZE << 2) + 2];
+
++#ifdef SQUASHFS_TRACE
+ long long obytes = bytes;
++#endif
+
+ for(i = 0; i < meta_blocks; i++) {
+ int avail_bytes = length > SQUASHFS_METADATA_SIZE ?
+@@ -2170,11 +2116,85 @@
+ }
+
+
++static int seq = 0;
++void reader_read_process(struct dir_ent *dir_ent)
++{
++ struct file_buffer *prev_buffer = NULL, *file_buffer;
++ int status, res, byte, count = 0;
++ int file = get_pseudo_file(dir_ent->inode->pseudo_id)->fd;
++ int child = get_pseudo_file(dir_ent->inode->pseudo_id)->child;
++ long long bytes = 0;
++
++ while(1) {
++ file_buffer = cache_get(reader_buffer, 0, 0);
++ file_buffer->sequence = seq ++;
++
++ byte = read_bytes(file, file_buffer->data, block_size);
++ if(byte == -1)
++ goto read_err;
++
++ file_buffer->size = byte;
++ file_buffer->file_size = -1;
++ file_buffer->block = count ++;
++ file_buffer->error = FALSE;
++ file_buffer->fragment = FALSE;
++ bytes += byte;
++
++ if(byte == 0)
++ break;
++
++ /*
++ * Update estimated_uncompressed block count. This is done
++ * on every block rather than waiting for all blocks to be
++ * read incase write_file_process() is running in parallel
++ * with this. Otherwise cur uncompressed block count may
++ * get ahead of the total uncompressed block count.
++ */
++ estimated_uncompressed ++;
++
++ if(prev_buffer)
++ queue_put(from_reader, prev_buffer);
++ prev_buffer = file_buffer;
++ }
++
++ /*
++ * Update inode file size now that the size of the dynamic pseudo file
++ * is known. This is needed for the -info option.
++ */
++ dir_ent->inode->buf.st_size = bytes;
++
++ res = waitpid(child, &status, 0);
++ if(res == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
++ goto read_err;
++
++ if(prev_buffer == NULL)
++ prev_buffer = file_buffer;
++ else {
++ cache_block_put(file_buffer);
++ seq --;
++ }
++ prev_buffer->file_size = bytes;
++ prev_buffer->fragment = !no_fragments &&
++ (count == 2 || always_use_fragments) && (byte < block_size);
++ queue_put(from_reader, prev_buffer);
++
++ return;
++
++read_err:
++ if(prev_buffer) {
++ cache_block_put(file_buffer);
++ seq --;
++ file_buffer = prev_buffer;
++ }
++ file_buffer->error = TRUE;
++ queue_put(from_deflate, file_buffer);
++}
++
++
+ void reader_read_file(struct dir_ent *dir_ent)
+ {
+ struct stat *buf = &dir_ent->inode->buf, buf2;
+ struct file_buffer *file_buffer;
+- static int index = 0;
+ int blocks, byte, count, expected, file, frag_block;
+ long long bytes, read_size;
+
+@@ -2202,7 +2222,7 @@
+ if(file_buffer)
+ queue_put(from_reader, file_buffer);
+ file_buffer = cache_get(reader_buffer, 0, 0);
+- file_buffer->sequence = index ++;
++ file_buffer->sequence = seq ++;
+
+ byte = file_buffer->size = read_bytes(file, file_buffer->data,
+ block_size);
+@@ -2238,7 +2258,7 @@
+
+ read_err:
+ file_buffer = cache_get(reader_buffer, 0, 0);
+- file_buffer->sequence = index ++;
++ file_buffer->sequence = seq ++;
+ read_err2:
+ file_buffer->error = TRUE;
+ queue_put(from_deflate, file_buffer);
+@@ -2262,9 +2282,14 @@
+ for(i = 0; i < dir->count; i++) {
+ struct dir_ent *dir_ent = dir->list[i];
+ struct stat *buf = &dir_ent->inode->buf;
+- if(dir_ent->data)
++ if(dir_ent->inode->root_entry)
+ continue;
+
++ if(dir_ent->inode->pseudo_file) {
++ reader_read_process(dir_ent);
++ continue;
++ }
++
+ switch(buf->st_mode & S_IFMT) {
+ case S_IFREG:
+ reader_read_file(dir_ent);
+@@ -2365,7 +2390,7 @@
+
+ void *deflator(void *arg)
+ {
+- z_stream *stream = NULL;
++ void *stream = NULL;
+ int oldstate;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+@@ -2402,7 +2427,7 @@
+
+ void *frag_deflator(void *arg)
+ {
+- z_stream *stream = NULL;
++ void *stream = NULL;
+ int oldstate;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+@@ -2426,8 +2451,8 @@
+ write_buffer->block = bytes;
+ bytes += compressed_size;
+ fragments_outstanding --;
+- pthread_mutex_unlock(&fragment_mutex);
+ queue_put(to_writer, write_buffer);
++ pthread_mutex_unlock(&fragment_mutex);
+ TRACE("Writing fragment %lld, uncompressed size %d, "
+ "compressed size %d\n", file_buffer->block,
+ file_buffer->size, compressed_size);
+@@ -2674,6 +2699,98 @@
+ }
+
+
++int write_file_process(squashfs_inode *inode, struct dir_ent *dir_ent,
++ struct file_buffer *read_buffer, int *duplicate_file)
++{
++ long long read_size, file_bytes, start;
++ struct fragment *fragment;
++ unsigned int *block_list = NULL;
++ int block = 0, status;
++ long long sparse = 0;
++ struct file_buffer *fragment_buffer = NULL;
++
++ *duplicate_file = FALSE;
++
++ lock_fragments();
++
++ file_bytes = 0;
++ start = bytes;
++ while (1) {
++ read_size = read_buffer->file_size;
++ if(read_buffer->fragment && read_buffer->c_byte)
++ fragment_buffer = read_buffer;
++ else {
++ block_list = realloc(block_list, (block + 1) *
++ sizeof(unsigned int));
++ if(block_list == NULL)
++ BAD_ERROR("Out of memory allocating block_list"
++ "\n");
++ block_list[block ++] = read_buffer->c_byte;
++ if(read_buffer->c_byte) {
++ read_buffer->block = bytes;
++ bytes += read_buffer->size;
++ cache_rehash(read_buffer, read_buffer->block);
++ file_bytes += read_buffer->size;
++ queue_put(to_writer, read_buffer);
++ } else {
++ sparse += read_buffer->size;
++ cache_block_put(read_buffer);
++ }
++ }
++ inc_progress_bar();
++
++ if(read_size != -1)
++ break;
++
++ read_buffer = get_file_buffer(from_deflate);
++ if(read_buffer->error)
++ goto read_err;
++ }
++
++ unlock_fragments();
++ fragment = get_and_fill_fragment(fragment_buffer);
++ cache_block_put(fragment_buffer);
++
++ if(duplicate_checking)
++ add_non_dup(read_size, file_bytes, block_list, start, fragment,
++ 0, 0, FALSE);
++ file_count ++;
++ total_bytes += read_size;
++
++ if(read_size < (1LL << 32) && start < (1LL << 32) && sparse == 0)
++ create_inode(inode, dir_ent, SQUASHFS_FILE_TYPE, read_size,
++ start, block, block_list, fragment, NULL, 0);
++ else
++ create_inode(inode, dir_ent, SQUASHFS_LREG_TYPE, read_size,
++ start, block, block_list, fragment, NULL, sparse);
++
++ if(duplicate_checking == FALSE)
++ free(block_list);
++
++ return 0;
++
++read_err:
++ cur_uncompressed -= block;
++ status = read_buffer->error;
++ bytes = start;
++ if(!block_device) {
++ int res;
++
++ queue_put(to_writer, NULL);
++ if(queue_get(from_writer) != 0)
++ EXIT_MKSQUASHFS();
++ res = ftruncate(fd, bytes);
++ if(res != 0)
++ BAD_ERROR("Failed to truncate dest file because %s\n",
++ strerror(errno));
++ }
++ unlock_fragments();
++ free(block_list);
++ cache_block_put(read_buffer);
++ return status;
++}
++
++
+ int write_file_blocks(squashfs_inode *inode, struct dir_ent *dir_ent,
+ long long read_size, struct file_buffer *read_buffer,
+ int *duplicate_file)
+@@ -2941,7 +3058,10 @@
+
+ read_size = read_buffer->file_size;
+
+- if(read_size == 0) {
++ if(read_size == -1)
++ status = write_file_process(inode, dir_ent, read_buffer,
++ duplicate_file);
++ else if(read_size == 0) {
+ write_file_empty(inode, dir_ent, duplicate_file);
+ cache_block_put(read_buffer);
+ } else if(read_buffer->fragment && read_buffer->c_byte)
+@@ -3036,6 +3156,8 @@
+
+ memcpy(&inode->buf, buf, sizeof(struct stat));
+ inode->read = FALSE;
++ inode->root_entry = FALSE;
++ inode->pseudo_file = FALSE;
+ inode->inode = SQUASHFS_INVALID_BLK;
+ inode->nlink = 1;
+
+@@ -3056,7 +3178,7 @@
+
+
+ inline void add_dir_entry(char *name, char *pathname, struct dir_info *sub_dir,
+- struct inode_info *inode_info, void *data, struct dir_info *dir)
++ struct inode_info *inode_info, struct dir_info *dir)
+ {
+ if((dir->count % DIR_ENTRIES) == 0) {
+ dir->list = realloc(dir->list, (dir->count + DIR_ENTRIES) *
+@@ -3075,8 +3197,7 @@
+ NULL;
+ dir->list[dir->count]->inode = inode_info;
+ dir->list[dir->count]->dir = sub_dir;
+- dir->list[dir->count]->our_dir = dir;
+- dir->list[dir->count++]->data = data;
++ dir->list[dir->count++]->our_dir = dir;
+ dir->byte_count += strlen(name) + sizeof(squashfs_dir_entry);
+ }
+
+@@ -3128,10 +3249,10 @@
+
+ if(dir->count < old_root_entries)
+ for(i = 0; i < old_root_entries; i++) {
+- if(old_root_entry[i].type == SQUASHFS_DIR_TYPE)
++ if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE)
+ dir->directory_count ++;
+- add_dir_entry(old_root_entry[i].name, "", NULL, NULL,
+- &old_root_entry[i], dir);
++ add_dir_entry(old_root_entry[i].name, "", NULL,
++ &old_root_entry[i].inode, dir);
+ }
+
+ while(index < source) {
+@@ -3167,10 +3288,10 @@
+
+ if(dir->count < old_root_entries)
+ for(i = 0; i < old_root_entries; i++) {
+- if(old_root_entry[i].type == SQUASHFS_DIR_TYPE)
++ if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE)
+ dir->directory_count ++;
+- add_dir_entry(old_root_entry[i].name, "", NULL, NULL,
+- &old_root_entry[i], dir);
++ add_dir_entry(old_root_entry[i].name, "", NULL,
++ &old_root_entry[i].inode, dir);
+ }
+
+ if((d_name = readdir(dir->linuxdir)) != NULL) {
+@@ -3215,7 +3336,7 @@
+ int current_count;
+
+ while((current_count = dir_info->current_count++) < dir_info->count)
+- if(dir_info->list[current_count]->data)
++ if(dir_info->list[current_count]->inode->root_entry)
+ continue;
+ else
+ return dir_info->list[current_count];
+@@ -3240,11 +3361,11 @@
+ int current_count;
+
+ while((current_count = dir_info->current_count++) < dir_info->count)
+- if(dir_info->list[current_count]->data)
+- add_dir(dir_info->list[current_count]->data->inode,
+- dir_info->list[current_count]->data->inode_number,
++ if(dir_info->list[current_count]->inode->root_entry)
++ add_dir(dir_info->list[current_count]->inode->inode,
++ dir_info->list[current_count]->inode->inode_number,
+ dir_info->list[current_count]->name,
+- dir_info->list[current_count]->data->type, dir);
++ dir_info->list[current_count]->inode->type, dir);
+ else
+ return dir_info->list[current_count];
+ return NULL;
+@@ -3313,7 +3434,6 @@
+ dir_ent->name = dir_ent->pathname = strdup(pathname);
+ dir_ent->dir = dir_info;
+ dir_ent->our_dir = NULL;
+- dir_ent->data = NULL;
+ dir_info->dir_ent = dir_ent;
+
+ if(sorted)
+@@ -3383,7 +3503,7 @@
+ sub_dir = NULL;
+
+ add_dir_entry(dir_name, filename, sub_dir, lookup_inode(&buf),
+- NULL, dir);
++ dir);
+ }
+
+ scan1_freedir(dir);
+@@ -3399,7 +3519,7 @@
+ struct dir_ent *dir_ent;
+ struct pseudo_entry *pseudo_ent;
+ struct stat buf;
+- static pseudo_ino = 1;
++ static int pseudo_ino = 1;
+
+ if(dir == NULL && (dir = scan1_opendir("")) == NULL)
+ return NULL;
+@@ -3415,6 +3535,29 @@
+
+ while((pseudo_ent = pseudo_readdir(pseudo)) != NULL) {
+ dir_ent = scan2_lookup(dir, pseudo_ent->name);
++ if(pseudo_ent->dev->type == 's') {
++ struct stat *buf;
++ if(dir_ent == NULL) {
++ ERROR("Pseudo set file \"%s\" does not exist "
++ "in source filesystem. Ignoring\n",
++ pseudo_ent->pathname);
++ continue;
++ }
++ if(dir_ent->inode->root_entry) {
++ ERROR("Pseudo set file \"%s\" is a pre-existing"
++ " file in the filesystem being appended"
++ " to. It cannot be modified. "
++ "Ignoring!\n", pseudo_ent->pathname);
++ continue;
++ }
++ buf = &dir_ent->inode->buf;
++ buf->st_mode = (buf->st_mode & S_IFMT) |
++ pseudo_ent->dev->mode;
++ buf->st_uid = pseudo_ent->dev->uid;
++ buf->st_gid = pseudo_ent->dev->gid;
++ continue;
++ }
++
+ if(dir_ent) {
+ ERROR("Pseudo file \"%s\" exists in source filesystem "
+ "\"%s\"\n", pseudo_ent->pathname,
+@@ -3444,8 +3587,29 @@
+ buf.st_mtime = time(NULL);
+ buf.st_ino = pseudo_ino ++;
+
+- add_dir_entry(pseudo_ent->name, pseudo_ent->pathname, sub_dir,
+- lookup_inode(&buf), NULL, dir);
++ if(pseudo_ent->dev->type == 'f') {
++#ifdef USE_TMP_FILE
++ struct stat buf2;
++ int res = stat(pseudo_ent->dev->filename, &buf2);
++ if(res == -1) {
++ ERROR("Stat on pseudo file \"%s\" failed, "
++ "skipping...", pseudo_ent->pathname);
++ continue;
++ }
++ buf.st_size = buf2.st_size;
++ add_dir_entry(pseudo_ent->name,
++ pseudo_ent->dev->filename, sub_dir,
++ lookup_inode(&buf), dir);
++#else
++ struct inode_info *inode = lookup_inode(&buf);
++ inode->pseudo_id = pseudo_ent->dev->pseudo_id;
++ inode->pseudo_file = TRUE;
++ add_dir_entry(pseudo_ent->name, pseudo_ent->pathname,
++ sub_dir, inode, dir);
++#endif
++ } else
++ add_dir_entry(pseudo_ent->name, pseudo_ent->pathname,
++ sub_dir, lookup_inode(&buf), dir);
+ }
+
+ scan2_freedir(dir);
+@@ -3482,8 +3646,9 @@
+ &duplicate_file);
+ INFO("file %s, uncompressed size %lld "
+ "bytes %s\n", filename,
+- buf->st_size, duplicate_file ?
+- "DUPLICATE" : "");
++ (long long) buf->st_size,
++ duplicate_file ? "DUPLICATE" :
++ "");
+ break;
+
+ case S_IFDIR:
+@@ -3557,6 +3722,7 @@
+ INFO("file %s, uncompressed "
+ "size %lld bytes LINK"
+ "\n", filename,
++ (long long)
+ buf->st_size);
+ break;
+ case SQUASHFS_SYMLINK_TYPE:
+@@ -3667,10 +3833,11 @@
+ BAD_ERROR("Out of memory in old root directory entries "
+ "reallocation\n");
+
+- strcpy(old_root_entry[old_root_entries].name, name);
+- old_root_entry[old_root_entries].inode = inode;
+- old_root_entry[old_root_entries].inode_number = inode_number;
+- old_root_entry[old_root_entries++].type = type;
++ old_root_entry[old_root_entries].name = strdup(name);
++ old_root_entry[old_root_entries].inode.inode = inode;
++ old_root_entry[old_root_entries].inode.inode_number = inode_number;
++ old_root_entry[old_root_entries].inode.type = type;
++ old_root_entry[old_root_entries++].inode.root_entry = TRUE;
+ }
+
+
+@@ -4137,7 +4304,7 @@
+
+
+ #define VERSION() \
+- printf("mksquashfs version 4.0 (2009/04/05)\n");\
++ printf("mksquashfs version 4.1-CVS (2009/09/20)\n");\
+ printf("copyright (C) 2009 Phillip Lougher <phillip@lougher.demon.co.uk>\n\n"); \
+ printf("This program is free software; you can redistribute it and/or\n");\
+ printf("modify it under the terms of the GNU General Public License\n");\
+@@ -4172,26 +4339,28 @@
+ source_path = argv + 1;
+ source = i - 2;
+ for(; i < argc; i++) {
+- if(strcmp(argv[i], "-pf") == 0) {
++ if(strcmp(argv[i], "-comp") == 0) {
+ if(++i == argc) {
+- ERROR("%s: -pf missing filename\n", argv[0]);
++ ERROR("%s: -comp missing compression type\n",
++ argv[0]);
+ exit(1);
+ }
+- if(read_pseudo_file(&pseudo, argv[i]) == FALSE) {
+- ERROR("Failed to parse pseudo file \"%s\"\n",
+- argv[i]);
++ comp_name = argv[i];
++ } else if(strcmp(argv[i], "-pf") == 0) {
++ if(++i == argc) {
++ ERROR("%s: -pf missing filename\n", argv[0]);
+ exit(1);
+ }
++ if(read_pseudo_file(&pseudo, argv[i]) == FALSE)
++ exit(1);
+ } else if(strcmp(argv[i], "-p") == 0) {
+ if(++i == argc) {
+ ERROR("%s: -p missing pseudo file definition\n",