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) 2009, 2010, 2012, 2014
      6  * Phillip Lougher <phillip (at) squashfs.org.uk>
      7  *
      8  * This program is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU General Public License
     10  * as published by the Free Software Foundation; either version 2,
     11  * or (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     21  *
     22  * pseudo.c
     23  */
     24 
     25 #include <pwd.h>
     26 #include <grp.h>
     27 #include <unistd.h>
     28 #include <stdio.h>
     29 #include <fcntl.h>
     30 #include <errno.h>
     31 #include <string.h>
     32 #include <stdlib.h>
     33 #include <sys/types.h>
     34 #include <sys/wait.h>
     35 #include <ctype.h>
     36 
     37 #include "pseudo.h"
     38 #include "error.h"
     39 #include "progressbar.h"
     40 
     41 #define TRUE 1
     42 #define FALSE 0
     43 
     44 extern int read_file(char *filename, char *type, int (parse_line)(char *));
     45 
     46 struct pseudo_dev **pseudo_file = NULL;
     47 struct pseudo *pseudo = NULL;
     48 int pseudo_count = 0;
     49 
     50 static char *get_component(char *target, char **targname)
     51 {
     52 	char *start;
     53 
     54 	while(*target == '/')
     55 		target ++;
     56 
     57 	start = target;
     58 	while(*target != '/' && *target != '\0')
     59 		target ++;
     60 
     61 	*targname = strndup(start, target - start);
     62 
     63 	while(*target == '/')
     64 		target ++;
     65 
     66 	return target;
     67 }
     68 
     69 
     70 /*
     71  * Add pseudo device target to the set of pseudo devices.  Pseudo_dev
     72  * describes the pseudo device attributes.
     73  */
     74 struct pseudo *add_pseudo(struct pseudo *pseudo, struct pseudo_dev *pseudo_dev,
     75 	char *target, char *alltarget)
     76 {
     77 	char *targname;
     78 	int i;
     79 
     80 	target = get_component(target, &targname);
     81 
     82 	if(pseudo == NULL) {
     83 		pseudo = malloc(sizeof(struct pseudo));
     84 		if(pseudo == NULL)
     85 			MEM_ERROR();
     86 
     87 		pseudo->names = 0;
     88 		pseudo->count = 0;
     89 		pseudo->name = NULL;
     90 	}
     91 
     92 	for(i = 0; i < pseudo->names; i++)
     93 		if(strcmp(pseudo->name[i].name, targname) == 0)
     94 			break;
     95 
     96 	if(i == pseudo->names) {
     97 		/* allocate new name entry */
     98 		pseudo->names ++;
     99 		pseudo->name = realloc(pseudo->name, (i + 1) *
    100 			sizeof(struct pseudo_entry));
    101 		if(pseudo->name == NULL)
    102 			MEM_ERROR();
    103 		pseudo->name[i].name = targname;
    104 
    105 		if(target[0] == '\0') {
    106 			/* at leaf pathname component */
    107 			pseudo->name[i].pseudo = NULL;
    108 			pseudo->name[i].pathname = strdup(alltarget);
    109 			pseudo->name[i].dev = pseudo_dev;
    110 		} else {
    111 			/* recurse adding child components */
    112 			pseudo->name[i].dev = NULL;
    113 			pseudo->name[i].pseudo = add_pseudo(NULL, pseudo_dev,
    114 				target, alltarget);
    115 		}
    116 	} else {
    117 		/* existing matching entry */
    118 		free(targname);
    119 
    120 		if(pseudo->name[i].pseudo == NULL) {
    121 			/* No sub-directory which means this is the leaf
    122 			 * component of a pre-existing pseudo file.
    123 			 */
    124 			if(target[0] != '\0') {
    125 				/*
    126 				 * entry must exist as either a 'd' type or
    127 				 * 'm' type pseudo file
    128 				 */
    129 				if(pseudo->name[i].dev->type == 'd' ||
    130 					pseudo->name[i].dev->type == 'm')
    131 					/* recurse adding child components */
    132 					pseudo->name[i].pseudo =
    133 						add_pseudo(NULL, pseudo_dev,
    134 						target, alltarget);
    135 				else {
    136 					ERROR_START("%s already exists as a "
    137 						"non directory.",
    138 						pseudo->name[i].name);
    139 					ERROR_EXIT(".  Ignoring %s!\n",
    140 						alltarget);
    141 				}
    142 			} else if(memcmp(pseudo_dev, pseudo->name[i].dev,
    143 					sizeof(struct pseudo_dev)) != 0) {
    144 				ERROR_START("%s already exists as a different "
    145 					"pseudo definition.", alltarget);
    146 				ERROR_EXIT("  Ignoring!\n");
    147 			} else {
    148 				ERROR_START("%s already exists as an identical "
    149 					"pseudo definition!", alltarget);
    150 				ERROR_EXIT("  Ignoring!\n");
    151 			}
    152 		} else {
    153 			if(target[0] == '\0') {
    154 				/*
    155 				 * sub-directory exists, which means we can only
    156 				 * add a pseudo file of type 'd' or type 'm'
    157 				 */
    158 				if(pseudo->name[i].dev == NULL &&
    159 						(pseudo_dev->type == 'd' ||
    160 						pseudo_dev->type == 'm')) {
    161 					pseudo->name[i].pathname =
    162 						strdup(alltarget);
    163 					pseudo->name[i].dev = pseudo_dev;
    164 				} else {
    165 					ERROR_START("%s already exists as a "
    166 						"different pseudo definition.",
    167 						pseudo->name[i].name);
    168 					ERROR_EXIT("  Ignoring %s!\n",
    169 						alltarget);
    170 				}
    171 			} else
    172 				/* recurse adding child components */
    173 				add_pseudo(pseudo->name[i].pseudo, pseudo_dev,
    174 					target, alltarget);
    175 		}
    176 	}
    177 
    178 	return pseudo;
    179 }
    180 
    181 
    182 /*
    183  * Find subdirectory in pseudo directory referenced by pseudo, matching
    184  * filename.  If filename doesn't exist or if filename is a leaf file
    185  * return NULL
    186  */
    187 struct pseudo *pseudo_subdir(char *filename, struct pseudo *pseudo)
    188 {
    189 	int i;
    190 
    191 	if(pseudo == NULL)
    192 		return NULL;
    193 
    194 	for(i = 0; i < pseudo->names; i++)
    195 		if(strcmp(filename, pseudo->name[i].name) == 0)
    196 			return pseudo->name[i].pseudo;
    197 
    198 	return NULL;
    199 }
    200 
    201 
    202 struct pseudo_entry *pseudo_readdir(struct pseudo *pseudo)
    203 {
    204 	if(pseudo == NULL)
    205 		return NULL;
    206 
    207 	while(pseudo->count < pseudo->names) {
    208 		if(pseudo->name[pseudo->count].dev != NULL)
    209 			return &pseudo->name[pseudo->count++];
    210 		else
    211 			pseudo->count++;
    212 	}
    213 
    214 	return NULL;
    215 }
    216 
    217 
    218 int pseudo_exec_file(struct pseudo_dev *dev, int *child)
    219 {
    220 	int res, pipefd[2];
    221 
    222 	res = pipe(pipefd);
    223 	if(res == -1) {
    224 		ERROR("Executing dynamic pseudo file, pipe failed\n");
    225 		return 0;
    226 	}
    227 
    228 	*child = fork();
    229 	if(*child == -1) {
    230 		ERROR("Executing dynamic pseudo file, fork failed\n");
    231 		goto failed;
    232 	}
    233 
    234 	if(*child == 0) {
    235 		close(pipefd[0]);
    236 		close(STDOUT_FILENO);
    237 		res = dup(pipefd[1]);
    238 		if(res == -1)
    239 			exit(EXIT_FAILURE);
    240 
    241 		execl("/bin/sh", "sh", "-c", dev->command, (char *) NULL);
    242 		exit(EXIT_FAILURE);
    243 	}
    244 
    245 	close(pipefd[1]);
    246 	return pipefd[0];
    247 
    248 failed:
    249 	close(pipefd[0]);
    250 	close(pipefd[1]);
    251 	return 0;
    252 }
    253 
    254 
    255 void add_pseudo_file(struct pseudo_dev *dev)
    256 {
    257 	pseudo_file = realloc(pseudo_file, (pseudo_count + 1) *
    258 		sizeof(struct pseudo_dev *));
    259 	if(pseudo_file == NULL)
    260 		MEM_ERROR();
    261 
    262 	dev->pseudo_id = pseudo_count;
    263 	pseudo_file[pseudo_count ++] = dev;
    264 }
    265 
    266 
    267 struct pseudo_dev *get_pseudo_file(int pseudo_id)
    268 {
    269 	return pseudo_file[pseudo_id];
    270 }
    271 
    272 
    273 int read_pseudo_def(char *def)
    274 {
    275 	int n, bytes;
    276 	unsigned int major = 0, minor = 0, mode;
    277 	char type, *ptr;
    278 	char suid[100], sgid[100]; /* overflow safe */
    279 	char *filename, *name;
    280 	char *orig_def = def;
    281 	long long uid, gid;
    282 	struct pseudo_dev *dev;
    283 
    284 	/*
    285 	 * Scan for filename, don't use sscanf() and "%s" because
    286 	 * that can't handle filenames with spaces
    287 	 */
    288 	filename = malloc(strlen(def) + 1);
    289 	if(filename == NULL)
    290 		MEM_ERROR();
    291 
    292 	for(name = filename; !isspace(*def) && *def != '\0';) {
    293 		if(*def == '\\') {
    294 			def ++;
    295 			if (*def == '\0')
    296 				break;
    297 		}
    298 		*name ++ = *def ++;
    299 	}
    300 	*name = '\0';
    301 
    302 	if(*filename == '\0') {
    303 		ERROR("Not enough or invalid arguments in pseudo file "
    304 			"definition \"%s\"\n", orig_def);
    305 		goto error;
    306 	}
    307 
    308 	n = sscanf(def, " %c %o %99s %99s %n", &type, &mode, suid, sgid,
    309 		&bytes);
    310 	def += bytes;
    311 
    312 	if(n < 4) {
    313 		ERROR("Not enough or invalid arguments in pseudo file "
    314 			"definition \"%s\"\n", orig_def);
    315 		switch(n) {
    316 		case -1:
    317 			/* FALLTHROUGH */
    318 		case 0:
    319 			ERROR("Read filename, but failed to read or match "
    320 				"type\n");
    321 			break;
    322 		case 1:
    323 			ERROR("Read filename and type, but failed to read or "
    324 				"match octal mode\n");
    325 			break;
    326 		case 2:
    327 			ERROR("Read filename, type and mode, but failed to "
    328 				"read or match uid\n");
    329 			break;
    330 		default:
    331 			ERROR("Read filename, type, mode and uid, but failed "
    332 				"to read or match gid\n");
    333 			break;
    334 		}
    335 		goto error;
    336 	}
    337 
    338 	switch(type) {
    339 	case 'b':
    340 		/* FALLTHROUGH */
    341 	case 'c':
    342 		n = sscanf(def, "%u %u %n", &major, &minor, &bytes);
    343 		def += bytes;
    344 
    345 		if(n < 2) {
    346 			ERROR("Not enough or invalid arguments in %s device "
    347 				"pseudo file definition \"%s\"\n", type == 'b' ?
    348 				"block" : "character", orig_def);
    349 			if(n < 1)
    350 				ERROR("Read filename, type, mode, uid and gid, "
    351 					"but failed to read or match major\n");
    352 			else
    353 				ERROR("Read filename, type, mode, uid, gid "
    354 					"and major, but failed to read  or "
    355 					"match minor\n");
    356 			goto error;
    357 		}
    358 
    359 		if(major > 0xfff) {
    360 			ERROR("Major %d out of range\n", major);
    361 			goto error;
    362 		}
    363 
    364 		if(minor > 0xfffff) {
    365 			ERROR("Minor %d out of range\n", minor);
    366 			goto error;
    367 		}
    368 		/* FALLTHROUGH */
    369 	case 'd':
    370 		/* FALLTHROUGH */
    371 	case 'm':
    372 		/*
    373 		 * Check for trailing junk after expected arguments
    374 		 */
    375 		if(def[0] != '\0') {
    376 			ERROR("Unexpected tailing characters in pseudo file "
    377 				"definition \"%s\"\n", orig_def);
    378 			goto error;
    379 		}
    380 		break;
    381 	case 'f':
    382 		if(def[0] == '\0') {
    383 			ERROR("Not enough arguments in dynamic file pseudo "
    384 				"definition \"%s\"\n", orig_def);
    385 			ERROR("Expected command, which can be an executable "
    386 				"or a piece of shell script\n");
    387 			goto error;
    388 		}
    389 		break;
    390 	default:
    391 		ERROR("Unsupported type %c\n", type);
    392 		goto error;
    393 	}
    394 
    395 
    396 	if(mode > 07777) {
    397 		ERROR("Mode %o out of range\n", mode);
    398 		goto error;
    399 	}
    400 
    401 	uid = strtoll(suid, &ptr, 10);
    402 	if(*ptr == '\0') {
    403 		if(uid < 0 || uid > ((1LL << 32) - 1)) {
    404 			ERROR("Uid %s out of range\n", suid);
    405 			goto error;
    406 		}
    407 	} else {
    408 		struct passwd *pwuid = getpwnam(suid);
    409 		if(pwuid)
    410 			uid = pwuid->pw_uid;
    411 		else {
    412 			ERROR("Uid %s invalid uid or unknown user\n", suid);
    413 			goto error;
    414 		}
    415 	}
    416 
    417 	gid = strtoll(sgid, &ptr, 10);
    418 	if(*ptr == '\0') {
    419 		if(gid < 0 || gid > ((1LL << 32) - 1)) {
    420 			ERROR("Gid %s out of range\n", sgid);
    421 			goto error;
    422 		}
    423 	} else {
    424 		struct group *grgid = getgrnam(sgid);
    425 		if(grgid)
    426 			gid = grgid->gr_gid;
    427 		else {
    428 			ERROR("Gid %s invalid uid or unknown user\n", sgid);
    429 			goto error;
    430 		}
    431 	}
    432 
    433 	switch(type) {
    434 	case 'b':
    435 		mode |= S_IFBLK;
    436 		break;
    437 	case 'c':
    438 		mode |= S_IFCHR;
    439 		break;
    440 	case 'd':
    441 		mode |= S_IFDIR;
    442 		break;
    443 	case 'f':
    444 		mode |= S_IFREG;
    445 		break;
    446 	}
    447 
    448 	dev = malloc(sizeof(struct pseudo_dev));
    449 	if(dev == NULL)
    450 		MEM_ERROR();
    451 
    452 	dev->type = type;
    453 	dev->mode = mode;
    454 	dev->uid = uid;
    455 	dev->gid = gid;
    456 	dev->major = major;
    457 	dev->minor = minor;
    458 	if(type == 'f') {
    459 		dev->command = strdup(def);
    460 		add_pseudo_file(dev);
    461 	}
    462 
    463 	pseudo = add_pseudo(pseudo, dev, filename, filename);
    464 
    465 	free(filename);
    466 	return TRUE;
    467 
    468 error:
    469 	ERROR("Pseudo definitions should be of format\n");
    470 	ERROR("\tfilename d mode uid gid\n");
    471 	ERROR("\tfilename m mode uid gid\n");
    472 	ERROR("\tfilename b mode uid gid major minor\n");
    473 	ERROR("\tfilename c mode uid gid major minor\n");
    474 	ERROR("\tfilename f mode uid command\n");
    475 	free(filename);
    476 	return FALSE;
    477 }
    478 
    479 
    480 int read_pseudo_file(char *filename)
    481 {
    482 	return read_file(filename, "pseudo", read_pseudo_def);
    483 }
    484 
    485 
    486 struct pseudo *get_pseudo()
    487 {
    488 	return pseudo;
    489 }
    490 
    491 
    492 #ifdef SQUASHFS_TRACE
    493 static void dump_pseudo(struct pseudo *pseudo, char *string)
    494 {
    495 	int i, res;
    496 	char *path;
    497 
    498 	for(i = 0; i < pseudo->names; i++) {
    499 		struct pseudo_entry *entry = &pseudo->name[i];
    500 		if(string) {
    501 			res = asprintf(&path, "%s/%s", string, entry->name);
    502 			if(res == -1)
    503 				BAD_ERROR("asprintf failed in dump_pseudo\n");
    504 		} else
    505 			path = entry->name;
    506 		if(entry->dev)
    507 			ERROR("%s %c 0%o %d %d %d %d\n", path, entry->dev->type,
    508 				entry->dev->mode & ~S_IFMT, entry->dev->uid,
    509 				entry->dev->gid, entry->dev->major,
    510 				entry->dev->minor);
    511 		if(entry->pseudo)
    512 			dump_pseudo(entry->pseudo, path);
    513 		if(string)
    514 			free(path);
    515 	}
    516 }
    517 
    518 
    519 void dump_pseudos()
    520 {
    521     if (pseudo)
    522         dump_pseudo(pseudo, NULL);
    523 }
    524 #else
    525 void dump_pseudos()
    526 {
    527 }
    528 #endif
    529