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