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@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
get_component(char * target,char ** targname)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 */
add_pseudo(struct pseudo * pseudo,struct pseudo_dev * pseudo_dev,char * target,char * alltarget)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 */
pseudo_subdir(char * filename,struct pseudo * pseudo)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
pseudo_readdir(struct pseudo * pseudo)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
pseudo_exec_file(struct pseudo_dev * dev,int * child)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
add_pseudo_file(struct pseudo_dev * dev)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
get_pseudo_file(int pseudo_id)268 struct pseudo_dev *get_pseudo_file(int pseudo_id)
269 {
270 return pseudo_file[pseudo_id];
271 }
272
273
read_pseudo_def(char * def)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
read_pseudo_file(char * filename)481 int read_pseudo_file(char *filename)
482 {
483 return read_file(filename, "pseudo", read_pseudo_def);
484 }
485
486
get_pseudo()487 struct pseudo *get_pseudo()
488 {
489 return pseudo;
490 }
491
492
493 #ifdef SQUASHFS_TRACE
dump_pseudo(struct pseudo * pseudo,char * string)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
dump_pseudos()520 void dump_pseudos()
521 {
522 if (pseudo)
523 dump_pseudo(pseudo, NULL);
524 }
525 #else
dump_pseudos()526 void dump_pseudos()
527 {
528 }
529 #endif
530