Home | History | Annotate | Download | only in squashfs-tools
      1 /*
      2  * Create a squashfs filesystem.  This is a highly compressed read only
      3  * filesystem.
      4  *
      5  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
      6  * 2013, 2014
      7  * Phillip Lougher <phillip (at) squashfs.org.uk>
      8  *
      9  * This program is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU General Public License
     11  * as published by the Free Software Foundation; either version 2,
     12  * or (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program; if not, write to the Free Software
     21  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     22  *
     23  * sort.c
     24  */
     25 
     26 #define TRUE 1
     27 #define FALSE 0
     28 #define MAX_LINE 16384
     29 
     30 #include <unistd.h>
     31 #include <stdio.h>
     32 #include <sys/types.h>
     33 #include <sys/stat.h>
     34 #include <fcntl.h>
     35 #include <errno.h>
     36 #include <dirent.h>
     37 #include <string.h>
     38 #include <stdlib.h>
     39 #include <ctype.h>
     40 
     41 #include "squashfs_fs.h"
     42 #include "mksquashfs.h"
     43 #include "sort.h"
     44 #include "error.h"
     45 #include "progressbar.h"
     46 
     47 int mkisofs_style = -1;
     48 
     49 struct sort_info {
     50 	dev_t			st_dev;
     51 	ino_t			st_ino;
     52 	int			priority;
     53 	struct sort_info	*next;
     54 };
     55 
     56 struct sort_info *sort_info_list[65536];
     57 
     58 struct priority_entry *priority_list[65536];
     59 
     60 extern int silent;
     61 extern void write_file(squashfs_inode *inode, struct dir_ent *dir_ent,
     62 	int *c_size);
     63 extern char *pathname(struct dir_ent *dir_ent);
     64 
     65 
     66 void add_priority_list(struct dir_ent *dir, int priority)
     67 {
     68 	struct priority_entry *new_priority_entry;
     69 
     70 	priority += 32768;
     71 	new_priority_entry = malloc(sizeof(struct priority_entry));
     72 	if(new_priority_entry == NULL)
     73 		MEM_ERROR();
     74 
     75 	new_priority_entry->dir = dir;;
     76 	new_priority_entry->next = priority_list[priority];
     77 	priority_list[priority] = new_priority_entry;
     78 }
     79 
     80 
     81 int get_priority(char *filename, struct stat *buf, int priority)
     82 {
     83 	int hash = buf->st_ino & 0xffff;
     84 	struct sort_info *s;
     85 
     86 	for(s = sort_info_list[hash]; s; s = s->next)
     87 		if((s->st_dev == buf->st_dev) && (s->st_ino == buf->st_ino)) {
     88 			TRACE("returning priority %d (%s)\n", s->priority,
     89 				filename);
     90 			return s->priority;
     91 		}
     92 	TRACE("returning priority %d (%s)\n", priority, filename);
     93 	return priority;
     94 }
     95 
     96 
     97 #define ADD_ENTRY(buf, priority) {\
     98 	int hash = buf.st_ino & 0xffff;\
     99 	struct sort_info *s;\
    100 	if((s = malloc(sizeof(struct sort_info))) == NULL) \
    101 		MEM_ERROR(); \
    102 	s->st_dev = buf.st_dev;\
    103 	s->st_ino = buf.st_ino;\
    104 	s->priority = priority;\
    105 	s->next = sort_info_list[hash];\
    106 	sort_info_list[hash] = s;\
    107 	}
    108 int add_sort_list(char *path, int priority, int source, char *source_path[])
    109 {
    110 	int i, n;
    111 	struct stat buf;
    112 
    113 	TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
    114 	if(strlen(path) > 1 && strcmp(path + strlen(path) - 2, "/*") == 0)
    115 		path[strlen(path) - 2] = '\0';
    116 
    117 	TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
    118 re_read:
    119 	if(path[0] == '/' || strncmp(path, "./", 2) == 0 ||
    120 			strncmp(path, "../", 3) == 0 || mkisofs_style == 1) {
    121 		if(lstat(path, &buf) == -1)
    122 			goto error;
    123 		TRACE("adding filename %s, priority %d, st_dev %d, st_ino "
    124 			"%lld\n", path, priority, (int) buf.st_dev,
    125 			(long long) buf.st_ino);
    126 		ADD_ENTRY(buf, priority);
    127 		return TRUE;
    128 	}
    129 
    130 	for(i = 0, n = 0; i < source; i++) {
    131 		char *filename;
    132 		int res = asprintf(&filename, "%s/%s", source_path[i], path);
    133 		if(res == -1)
    134 			BAD_ERROR("asprintf failed in add_sort_list\n");
    135 		res = lstat(filename, &buf);
    136 		free(filename);
    137 		if(res == -1) {
    138 			if(!(errno == ENOENT || errno == ENOTDIR))
    139 				goto error;
    140 			continue;
    141 		}
    142 		ADD_ENTRY(buf, priority);
    143 		n ++;
    144 	}
    145 
    146 	if(n == 0 && mkisofs_style == -1 && lstat(path, &buf) != -1) {
    147 		ERROR("WARNING: Mkisofs style sortlist detected! This is "
    148 			"supported but please\n");
    149 		ERROR("convert to mksquashfs style sortlist! A sortlist entry");
    150 	        ERROR(" should be\neither absolute (starting with ");
    151 		ERROR("'/') start with './' or '../' (taken to be\nrelative to "
    152 			"$PWD), otherwise it ");
    153 		ERROR("is assumed the entry is relative to one\nof the source "
    154 			"directories, i.e. with ");
    155 		ERROR("\"mksquashfs test test.sqsh\",\nthe sortlist ");
    156 		ERROR("entry \"file\" is assumed to be inside the directory "
    157 			"test.\n\n");
    158 		mkisofs_style = 1;
    159 		goto re_read;
    160 	}
    161 
    162 	mkisofs_style = 0;
    163 
    164 	if(n == 1)
    165 		return TRUE;
    166 	if(n > 1) {
    167 		ERROR(" Ambiguous sortlist entry \"%s\"\n\nIt maps to more "
    168 			"than one source entry!  Please use an absolute path."
    169 			"\n", path);
    170 		return FALSE;
    171 	}
    172 
    173 error:
    174         ERROR_START("Cannot stat sortlist entry \"%s\"\n", path);
    175         ERROR("This is probably because you're using the wrong file\n");
    176         ERROR("path relative to the source directories.");
    177 	ERROR_EXIT("  Ignoring");
    178 	/*
    179 	 * Historical note
    180 	 * Failure to stat a sortlist entry is deliberately ignored, even
    181 	 * though it is an error.  Squashfs release 2.2 changed the behaviour
    182 	 * to treat it as a fatal error, but it was changed back to
    183 	 * the original behaviour to ignore it in release 2.2-r2 following
    184 	 * feedback from users at the time.
    185 	 */
    186         return TRUE;
    187 }
    188 
    189 
    190 void generate_file_priorities(struct dir_info *dir, int priority,
    191 	struct stat *buf)
    192 {
    193 	struct dir_ent *dir_ent = dir->list;
    194 
    195 	priority = get_priority(dir->pathname, buf, priority);
    196 
    197 	for(; dir_ent; dir_ent = dir_ent->next) {
    198 		struct stat *buf = &dir_ent->inode->buf;
    199 		if(dir_ent->inode->root_entry)
    200 			continue;
    201 
    202 		switch(buf->st_mode & S_IFMT) {
    203 			case S_IFREG:
    204 				add_priority_list(dir_ent,
    205 					get_priority(pathname(dir_ent), buf,
    206 					priority));
    207 				break;
    208 			case S_IFDIR:
    209 				generate_file_priorities(dir_ent->dir,
    210 					priority, buf);
    211 				break;
    212 		}
    213 	}
    214 }
    215 
    216 
    217 int read_sort_file(char *filename, int source, char *source_path[])
    218 {
    219 	FILE *fd;
    220 	char line_buffer[MAX_LINE + 1]; /* overflow safe */
    221 	char sort_filename[MAX_LINE + 1]; /* overflow safe */
    222 	char *line, *name;
    223 	int n, priority, res;
    224 
    225 	if((fd = fopen(filename, "r")) == NULL) {
    226 		ERROR("Failed to open sort file \"%s\" because %s\n",
    227 			filename, strerror(errno));
    228 		return FALSE;
    229 	}
    230 
    231 	while(fgets(line = line_buffer, MAX_LINE + 1, fd) != NULL) {
    232 		int len = strlen(line);
    233 
    234 		if(len == MAX_LINE && line[len - 1] != '\n') {
    235 			/* line too large */
    236 			ERROR("Line too long when reading "
    237 				"sort file \"%s\", larger than %d "
    238 				"bytes\n", filename, MAX_LINE);
    239 			goto failed;
    240 		}
    241 
    242 		/*
    243 		 * Remove '\n' terminator if it exists (the last line
    244 		 * in the file may not be '\n' terminated)
    245 		 */
    246 		if(len && line[len - 1] == '\n')
    247 			line[len - 1] = '\0';
    248 
    249 		/* Skip any leading whitespace */
    250 		while(isspace(*line))
    251 			line ++;
    252 
    253 		/* if comment line, skip */
    254 		if(*line == '#')
    255 			continue;
    256 
    257 		/*
    258 		 * Scan for filename, don't use sscanf() and "%s" because
    259 		 * that can't handle filenames with spaces
    260 		 */
    261 		for(name = sort_filename; !isspace(*line) && *line != '\0';) {
    262 			if(*line == '\\') {
    263 				line ++;
    264 				if (*line == '\0')
    265 					break;
    266 			}
    267 			*name ++ = *line ++;
    268 		}
    269 		*name = '\0';
    270 
    271 		/*
    272 		 * if filename empty, then line was empty of anything but
    273 		 * whitespace or a backslash character.  Skip empy lines
    274 		 */
    275 		if(sort_filename[0] == '\0')
    276 			continue;
    277 
    278 		/*
    279 		 * Scan the rest of the line, we expect a decimal number
    280 		 * which is the filename priority
    281 		 */
    282 		errno = 0;
    283 		res = sscanf(line, "%d%n", &priority, &n);
    284 
    285 		if((res < 1 || errno) && errno != ERANGE) {
    286 			if(errno == 0)
    287 				/* No error, assume EOL or match failure */
    288 				ERROR("Sort file \"%s\", can't find priority "
    289 					"in entry \"%s\", EOL or match "
    290 					"failure\n", filename, line_buffer);
    291 			else
    292 				/* Some other failure not ERANGE */
    293 				ERROR("Sscanf failed reading sort file \"%s\" "
    294 					"because %s\n", filename,
    295 					strerror(errno));
    296 			goto failed;
    297 		} else if((errno == ERANGE) ||
    298 				(priority < -32768 || priority > 32767)) {
    299 			ERROR("Sort file \"%s\", entry \"%s\" has priority "
    300 				"outside range of -32767:32768.\n", filename,
    301 				line_buffer);
    302 			goto failed;
    303 		}
    304 
    305 		/* Skip any trailing whitespace */
    306 		line += n;
    307 		while(isspace(*line))
    308 			line ++;
    309 
    310 		if(*line != '\0') {
    311 			ERROR("Sort file \"%s\", trailing characters after "
    312 				"priority in entry \"%s\"\n", filename,
    313 				line_buffer);
    314 			goto failed;
    315 		}
    316 
    317 		res = add_sort_list(sort_filename, priority, source,
    318 			source_path);
    319 		if(res == FALSE)
    320 			goto failed;
    321 	}
    322 
    323 	if(ferror(fd)) {
    324 		ERROR("Reading sort file \"%s\" failed because %s\n", filename,
    325 			strerror(errno));
    326 		goto failed;
    327 	}
    328 
    329 	fclose(fd);
    330 	return TRUE;
    331 
    332 failed:
    333 	fclose(fd);
    334 	return FALSE;
    335 }
    336 
    337 
    338 void sort_files_and_write(struct dir_info *dir)
    339 {
    340 	int i;
    341 	struct priority_entry *entry;
    342 	squashfs_inode inode;
    343 	int duplicate_file;
    344 
    345 	for(i = 65535; i >= 0; i--)
    346 		for(entry = priority_list[i]; entry; entry = entry->next) {
    347 			TRACE("%d: %s\n", i - 32768, pathname(entry->dir));
    348 			if(entry->dir->inode->inode == SQUASHFS_INVALID_BLK) {
    349 				write_file(&inode, entry->dir, &duplicate_file);
    350 				INFO("file %s, uncompressed size %lld bytes %s"
    351 					"\n", pathname(entry->dir),
    352 					(long long)
    353 					entry->dir->inode->buf.st_size,
    354 					duplicate_file ? "DUPLICATE" : "");
    355 				entry->dir->inode->inode = inode;
    356 				entry->dir->inode->type = SQUASHFS_FILE_TYPE;
    357 			} else
    358 				INFO("file %s, uncompressed size %lld bytes "
    359 					"LINK\n", pathname(entry->dir),
    360 					(long long)
    361 					entry->dir->inode->buf.st_size);
    362 		}
    363 }
    364