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