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