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@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 
add_priority_list(struct dir_ent * dir,int priority)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 
get_priority(char * filename,struct stat * buf,int priority)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 	}
add_sort_list(char * path,int priority,int source,char * source_path[])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 
generate_file_priorities(struct dir_info * dir,int priority,struct stat * buf)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 
read_sort_file(char * filename,int source,char * source_path[])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 
sort_files_and_write(struct dir_info * dir)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