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