mirror of https://github.com/ventoy/Ventoy
364 lines
9.3 KiB
C
364 lines
9.3 KiB
C
/*
|
|
* Create a squashfs filesystem. This is a highly compressed read only
|
|
* filesystem.
|
|
*
|
|
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
|
|
* 2013, 2014
|
|
* Phillip Lougher <phillip@squashfs.org.uk>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2,
|
|
* or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* sort.c
|
|
*/
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#define MAX_LINE 16384
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#include "squashfs_fs.h"
|
|
#include "mksquashfs.h"
|
|
#include "sort.h"
|
|
#include "error.h"
|
|
#include "progressbar.h"
|
|
|
|
int mkisofs_style = -1;
|
|
|
|
struct sort_info {
|
|
dev_t st_dev;
|
|
ino_t st_ino;
|
|
int priority;
|
|
struct sort_info *next;
|
|
};
|
|
|
|
struct sort_info *sort_info_list[65536];
|
|
|
|
struct priority_entry *priority_list[65536];
|
|
|
|
extern int silent;
|
|
extern void write_file(squashfs_inode *inode, struct dir_ent *dir_ent,
|
|
int *c_size);
|
|
extern char *pathname(struct dir_ent *dir_ent);
|
|
|
|
|
|
void add_priority_list(struct dir_ent *dir, int priority)
|
|
{
|
|
struct priority_entry *new_priority_entry;
|
|
|
|
priority += 32768;
|
|
new_priority_entry = malloc(sizeof(struct priority_entry));
|
|
if(new_priority_entry == NULL)
|
|
MEM_ERROR();
|
|
|
|
new_priority_entry->dir = dir;;
|
|
new_priority_entry->next = priority_list[priority];
|
|
priority_list[priority] = new_priority_entry;
|
|
}
|
|
|
|
|
|
int get_priority(char *filename, struct stat *buf, int priority)
|
|
{
|
|
int hash = buf->st_ino & 0xffff;
|
|
struct sort_info *s;
|
|
|
|
for(s = sort_info_list[hash]; s; s = s->next)
|
|
if((s->st_dev == buf->st_dev) && (s->st_ino == buf->st_ino)) {
|
|
TRACE("returning priority %d (%s)\n", s->priority,
|
|
filename);
|
|
return s->priority;
|
|
}
|
|
TRACE("returning priority %d (%s)\n", priority, filename);
|
|
return priority;
|
|
}
|
|
|
|
|
|
#define ADD_ENTRY(buf, priority) {\
|
|
int hash = buf.st_ino & 0xffff;\
|
|
struct sort_info *s;\
|
|
if((s = malloc(sizeof(struct sort_info))) == NULL) \
|
|
MEM_ERROR(); \
|
|
s->st_dev = buf.st_dev;\
|
|
s->st_ino = buf.st_ino;\
|
|
s->priority = priority;\
|
|
s->next = sort_info_list[hash];\
|
|
sort_info_list[hash] = s;\
|
|
}
|
|
int add_sort_list(char *path, int priority, int source, char *source_path[])
|
|
{
|
|
int i, n;
|
|
struct stat buf;
|
|
|
|
TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
|
|
if(strlen(path) > 1 && strcmp(path + strlen(path) - 2, "/*") == 0)
|
|
path[strlen(path) - 2] = '\0';
|
|
|
|
TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
|
|
re_read:
|
|
if(path[0] == '/' || strncmp(path, "./", 2) == 0 ||
|
|
strncmp(path, "../", 3) == 0 || mkisofs_style == 1) {
|
|
if(lstat(path, &buf) == -1)
|
|
goto error;
|
|
TRACE("adding filename %s, priority %d, st_dev %d, st_ino "
|
|
"%lld\n", path, priority, (int) buf.st_dev,
|
|
(long long) buf.st_ino);
|
|
ADD_ENTRY(buf, priority);
|
|
return TRUE;
|
|
}
|
|
|
|
for(i = 0, n = 0; i < source; i++) {
|
|
char *filename;
|
|
int res = asprintf(&filename, "%s/%s", source_path[i], path);
|
|
if(res == -1)
|
|
BAD_ERROR("asprintf failed in add_sort_list\n");
|
|
res = lstat(filename, &buf);
|
|
free(filename);
|
|
if(res == -1) {
|
|
if(!(errno == ENOENT || errno == ENOTDIR))
|
|
goto error;
|
|
continue;
|
|
}
|
|
ADD_ENTRY(buf, priority);
|
|
n ++;
|
|
}
|
|
|
|
if(n == 0 && mkisofs_style == -1 && lstat(path, &buf) != -1) {
|
|
ERROR("WARNING: Mkisofs style sortlist detected! This is "
|
|
"supported but please\n");
|
|
ERROR("convert to mksquashfs style sortlist! A sortlist entry");
|
|
ERROR(" should be\neither absolute (starting with ");
|
|
ERROR("'/') start with './' or '../' (taken to be\nrelative to "
|
|
"$PWD), otherwise it ");
|
|
ERROR("is assumed the entry is relative to one\nof the source "
|
|
"directories, i.e. with ");
|
|
ERROR("\"mksquashfs test test.sqsh\",\nthe sortlist ");
|
|
ERROR("entry \"file\" is assumed to be inside the directory "
|
|
"test.\n\n");
|
|
mkisofs_style = 1;
|
|
goto re_read;
|
|
}
|
|
|
|
mkisofs_style = 0;
|
|
|
|
if(n == 1)
|
|
return TRUE;
|
|
if(n > 1) {
|
|
ERROR(" Ambiguous sortlist entry \"%s\"\n\nIt maps to more "
|
|
"than one source entry! Please use an absolute path."
|
|
"\n", path);
|
|
return FALSE;
|
|
}
|
|
|
|
error:
|
|
ERROR_START("Cannot stat sortlist entry \"%s\"\n", path);
|
|
ERROR("This is probably because you're using the wrong file\n");
|
|
ERROR("path relative to the source directories.");
|
|
ERROR_EXIT(" Ignoring");
|
|
/*
|
|
* Historical note
|
|
* Failure to stat a sortlist entry is deliberately ignored, even
|
|
* though it is an error. Squashfs release 2.2 changed the behaviour
|
|
* to treat it as a fatal error, but it was changed back to
|
|
* the original behaviour to ignore it in release 2.2-r2 following
|
|
* feedback from users at the time.
|
|
*/
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void generate_file_priorities(struct dir_info *dir, int priority,
|
|
struct stat *buf)
|
|
{
|
|
struct dir_ent *dir_ent = dir->list;
|
|
|
|
priority = get_priority(dir->pathname, buf, priority);
|
|
|
|
for(; dir_ent; dir_ent = dir_ent->next) {
|
|
struct stat *buf = &dir_ent->inode->buf;
|
|
if(dir_ent->inode->root_entry)
|
|
continue;
|
|
|
|
switch(buf->st_mode & S_IFMT) {
|
|
case S_IFREG:
|
|
add_priority_list(dir_ent,
|
|
get_priority(pathname(dir_ent), buf,
|
|
priority));
|
|
break;
|
|
case S_IFDIR:
|
|
generate_file_priorities(dir_ent->dir,
|
|
priority, buf);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int read_sort_file(char *filename, int source, char *source_path[])
|
|
{
|
|
FILE *fd;
|
|
char line_buffer[MAX_LINE + 1]; /* overflow safe */
|
|
char sort_filename[MAX_LINE + 1]; /* overflow safe */
|
|
char *line, *name;
|
|
int n, priority, res;
|
|
|
|
if((fd = fopen(filename, "r")) == NULL) {
|
|
ERROR("Failed to open sort file \"%s\" because %s\n",
|
|
filename, strerror(errno));
|
|
return FALSE;
|
|
}
|
|
|
|
while(fgets(line = line_buffer, MAX_LINE + 1, fd) != NULL) {
|
|
int len = strlen(line);
|
|
|
|
if(len == MAX_LINE && line[len - 1] != '\n') {
|
|
/* line too large */
|
|
ERROR("Line too long when reading "
|
|
"sort file \"%s\", larger than %d "
|
|
"bytes\n", filename, MAX_LINE);
|
|
goto failed;
|
|
}
|
|
|
|
/*
|
|
* Remove '\n' terminator if it exists (the last line
|
|
* in the file may not be '\n' terminated)
|
|
*/
|
|
if(len && line[len - 1] == '\n')
|
|
line[len - 1] = '\0';
|
|
|
|
/* Skip any leading whitespace */
|
|
while(isspace(*line))
|
|
line ++;
|
|
|
|
/* if comment line, skip */
|
|
if(*line == '#')
|
|
continue;
|
|
|
|
/*
|
|
* Scan for filename, don't use sscanf() and "%s" because
|
|
* that can't handle filenames with spaces
|
|
*/
|
|
for(name = sort_filename; !isspace(*line) && *line != '\0';) {
|
|
if(*line == '\\') {
|
|
line ++;
|
|
if (*line == '\0')
|
|
break;
|
|
}
|
|
*name ++ = *line ++;
|
|
}
|
|
*name = '\0';
|
|
|
|
/*
|
|
* if filename empty, then line was empty of anything but
|
|
* whitespace or a backslash character. Skip empy lines
|
|
*/
|
|
if(sort_filename[0] == '\0')
|
|
continue;
|
|
|
|
/*
|
|
* Scan the rest of the line, we expect a decimal number
|
|
* which is the filename priority
|
|
*/
|
|
errno = 0;
|
|
res = sscanf(line, "%d%n", &priority, &n);
|
|
|
|
if((res < 1 || errno) && errno != ERANGE) {
|
|
if(errno == 0)
|
|
/* No error, assume EOL or match failure */
|
|
ERROR("Sort file \"%s\", can't find priority "
|
|
"in entry \"%s\", EOL or match "
|
|
"failure\n", filename, line_buffer);
|
|
else
|
|
/* Some other failure not ERANGE */
|
|
ERROR("Sscanf failed reading sort file \"%s\" "
|
|
"because %s\n", filename,
|
|
strerror(errno));
|
|
goto failed;
|
|
} else if((errno == ERANGE) ||
|
|
(priority < -32768 || priority > 32767)) {
|
|
ERROR("Sort file \"%s\", entry \"%s\" has priority "
|
|
"outside range of -32768:32767.\n", filename,
|
|
line_buffer);
|
|
goto failed;
|
|
}
|
|
|
|
/* Skip any trailing whitespace */
|
|
line += n;
|
|
while(isspace(*line))
|
|
line ++;
|
|
|
|
if(*line != '\0') {
|
|
ERROR("Sort file \"%s\", trailing characters after "
|
|
"priority in entry \"%s\"\n", filename,
|
|
line_buffer);
|
|
goto failed;
|
|
}
|
|
|
|
res = add_sort_list(sort_filename, priority, source,
|
|
source_path);
|
|
if(res == FALSE)
|
|
goto failed;
|
|
}
|
|
|
|
if(ferror(fd)) {
|
|
ERROR("Reading sort file \"%s\" failed because %s\n", filename,
|
|
strerror(errno));
|
|
goto failed;
|
|
}
|
|
|
|
fclose(fd);
|
|
return TRUE;
|
|
|
|
failed:
|
|
fclose(fd);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void sort_files_and_write(struct dir_info *dir)
|
|
{
|
|
int i;
|
|
struct priority_entry *entry;
|
|
squashfs_inode inode;
|
|
int duplicate_file;
|
|
|
|
for(i = 65535; i >= 0; i--)
|
|
for(entry = priority_list[i]; entry; entry = entry->next) {
|
|
TRACE("%d: %s\n", i - 32768, pathname(entry->dir));
|
|
if(entry->dir->inode->inode == SQUASHFS_INVALID_BLK) {
|
|
write_file(&inode, entry->dir, &duplicate_file);
|
|
INFO("file %s, uncompressed size %lld bytes %s"
|
|
"\n", pathname(entry->dir),
|
|
(long long)
|
|
entry->dir->inode->buf.st_size,
|
|
duplicate_file ? "DUPLICATE" : "");
|
|
entry->dir->inode->inode = inode;
|
|
entry->dir->inode->type = SQUASHFS_FILE_TYPE;
|
|
} else
|
|
INFO("file %s, uncompressed size %lld bytes "
|
|
"LINK\n", pathname(entry->dir),
|
|
(long long)
|
|
entry->dir->inode->buf.st_size);
|
|
}
|
|
}
|