1 /*
2 * Copyright (c) 2007 Vreixo Formoso
3 * Copyright (c) 2011 - 2015 Thomas Schmitt
4 *
5 * This file is part of the libisofs project; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License version 2
7 * or later as published by the Free Software Foundation.
8 * See COPYING file for details.
9 */
10
11 /*
12 * Functions that act on the iso tree.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "../config.h"
17 #endif
18
19 #include "libisofs.h"
20 #include "node.h"
21 #include "image.h"
22 #include "fsource.h"
23 #include "builder.h"
24 #include "messages.h"
25 #include "tree.h"
26 #include "util.h"
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <fnmatch.h>
34
35
36 /**
37 * Add a new directory to the iso tree.
38 *
39 * @param parent
40 * the dir where the new directory will be created
41 * @param name
42 * name for the new dir. If a node with same name already exists on
43 * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
44 * @param dir
45 * place where to store a pointer to the newly created dir. No extra
46 * ref is added, so you will need to call iso_node_ref() if you really
47 * need it. You can pass NULL in this parameter if you don't need the
48 * pointer.
49 * @return
50 * number of nodes in dir if success, < 0 otherwise
51 * Possible errors:
52 * ISO_NULL_POINTER, if parent or name are NULL
53 * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
54 */
iso_tree_add_new_dir(IsoDir * parent,const char * name,IsoDir ** dir)55 int iso_tree_add_new_dir(IsoDir *parent, const char *name, IsoDir **dir)
56 {
57 int ret;
58 char *n;
59 IsoDir *node;
60 IsoNode **pos;
61 time_t now;
62
63 if (parent == NULL || name == NULL) {
64 return ISO_NULL_POINTER;
65 }
66 if (dir) {
67 *dir = NULL;
68 }
69
70 /* find place where to insert and check if it exists */
71 if (iso_dir_exists(parent, name, &pos)) {
72 /* a node with same name already exists */
73 return ISO_NODE_NAME_NOT_UNIQUE;
74 }
75
76 n = strdup(name);
77 ret = iso_node_new_dir(n, &node);
78 if (ret < 0) {
79 free(n);
80 return ret;
81 }
82
83 /* permissions from parent */
84 iso_node_set_permissions((IsoNode*)node, parent->node.mode);
85 iso_node_set_uid((IsoNode*)node, parent->node.uid);
86 iso_node_set_gid((IsoNode*)node, parent->node.gid);
87 iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
88
89 /* current time */
90 iso_nowtime(&now, 0);
91 iso_node_set_atime((IsoNode*)node, now);
92 iso_node_set_ctime((IsoNode*)node, now);
93 iso_node_set_mtime((IsoNode*)node, now);
94
95 if (dir) {
96 *dir = node;
97 }
98
99 /* add to dir */
100 return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
101 }
102
iso_image_add_new_dir(IsoImage * image,IsoDir * parent,const char * name,IsoDir ** dir)103 int iso_image_add_new_dir(IsoImage *image, IsoDir *parent, const char *name,
104 IsoDir **dir)
105 {
106 int ret;
107 char *namept;
108
109 ret = iso_image_truncate_name(image, name, &namept, 0);
110 if (ret < 0)
111 return ret;
112 ret = iso_tree_add_new_dir(parent, namept, dir);
113 return ret;
114 }
115
116 /**
117 * Add a new symlink to the directory tree. Permissions are set to 0777,
118 * owner and hidden atts are taken from parent. You can modify any of them
119 * later.
120 *
121 * @param parent
122 * the dir where the new symlink will be created
123 * @param name
124 * name for the new dir. If a node with same name already exists on
125 * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
126 * @param dest
127 * destination of the link
128 * @param link
129 * place where to store a pointer to the newly created link. No extra
130 * ref is added, so you will need to call iso_node_ref() if you really
131 * need it. You can pass NULL in this parameter if you don't need the
132 * pointer
133 * @return
134 * number of nodes in parent if success, < 0 otherwise
135 * Possible errors:
136 * ISO_NULL_POINTER, if parent, name or dest are NULL
137 * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
138 * ISO_OUT_OF_MEM
139 */
iso_tree_add_new_symlink(IsoDir * parent,const char * name,const char * dest,IsoSymlink ** link)140 int iso_tree_add_new_symlink(IsoDir *parent, const char *name,
141 const char *dest, IsoSymlink **link)
142 {
143 int ret;
144 char *n, *d;
145 IsoSymlink *node;
146 IsoNode **pos;
147 time_t now;
148
149 if (parent == NULL || name == NULL || dest == NULL) {
150 return ISO_NULL_POINTER;
151 }
152 if (link) {
153 *link = NULL;
154 }
155
156 /* find place where to insert */
157 if (iso_dir_exists(parent, name, &pos)) {
158 /* a node with same name already exists */
159 return ISO_NODE_NAME_NOT_UNIQUE;
160 }
161
162 n = strdup(name);
163 d = strdup(dest);
164 ret = iso_node_new_symlink(n, d, &node);
165 if (ret < 0) {
166 free(n);
167 free(d);
168 return ret;
169 }
170
171 /* permissions from parent */
172 iso_node_set_permissions((IsoNode*)node, 0777);
173 iso_node_set_uid((IsoNode*)node, parent->node.uid);
174 iso_node_set_gid((IsoNode*)node, parent->node.gid);
175 iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
176
177 /* current time */
178 iso_nowtime(&now, 0);
179 iso_node_set_atime((IsoNode*)node, now);
180 iso_node_set_ctime((IsoNode*)node, now);
181 iso_node_set_mtime((IsoNode*)node, now);
182
183 if (link) {
184 *link = node;
185 }
186
187 /* add to dir */
188 return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
189 }
190
iso_image_add_new_symlink(IsoImage * image,IsoDir * parent,const char * name,const char * dest,IsoSymlink ** link)191 int iso_image_add_new_symlink(IsoImage *image, IsoDir *parent,
192 const char *name, const char *dest,
193 IsoSymlink **link)
194 {
195 int ret;
196 char *namept;
197
198 ret = iso_image_truncate_name(image, name, &namept, 0);
199 if (ret < 0)
200 return ret;
201 ret = iso_tree_add_new_symlink(parent, namept, dest, link);
202 return ret;
203 }
204
205 /**
206 * Add a new special file to the directory tree. As far as libisofs concerns,
207 * an special file is a block device, a character device, a FIFO (named pipe)
208 * or a socket. You can choose the specific kind of file you want to add
209 * by setting mode properly (see man 2 stat).
210 *
211 * Note that special files are only written to image when Rock Ridge
212 * extensions are enabled. Moreover, a special file is just a directory entry
213 * in the image tree, no data is written beyond that.
214 *
215 * Owner and hidden atts are taken from parent. You can modify any of them
216 * later.
217 *
218 * @param parent
219 * the dir where the new special file will be created
220 * @param name
221 * name for the new special file. If a node with same name already exists
222 * on parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
223 * @param mode
224 * file type and permissions for the new node. Note that you can't
225 * specify any kind of file here, only special types are allowed. i.e,
226 * S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK,
227 * S_IFREG and S_IFDIR aren't.
228 * @param dev
229 * device ID, equivalent to the st_rdev field in man 2 stat.
230 * @param special
231 * place where to store a pointer to the newly created special file. No
232 * extra ref is added, so you will need to call iso_node_ref() if you
233 * really need it. You can pass NULL in this parameter if you don't need
234 * the pointer.
235 * @return
236 * number of nodes in parent if success, < 0 otherwise
237 * Possible errors:
238 * ISO_NULL_POINTER, if parent, name or dest are NULL
239 * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
240 * ISO_OUT_OF_MEM
241 *
242 */
iso_tree_add_new_special(IsoDir * parent,const char * name,mode_t mode,dev_t dev,IsoSpecial ** special)243 int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode,
244 dev_t dev, IsoSpecial **special)
245 {
246 int ret;
247 char *n;
248 IsoSpecial *node;
249 IsoNode **pos;
250 time_t now;
251
252 if (parent == NULL || name == NULL) {
253 return ISO_NULL_POINTER;
254 }
255 if (S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)) {
256 return ISO_WRONG_ARG_VALUE;
257 }
258 if (special) {
259 *special = NULL;
260 }
261
262 /* find place where to insert */
263 if (iso_dir_exists(parent, name, &pos)) {
264 /* a node with same name already exists */
265 return ISO_NODE_NAME_NOT_UNIQUE;
266 }
267
268 n = strdup(name);
269 ret = iso_node_new_special(n, mode, dev, &node);
270 if (ret < 0) {
271 free(n);
272 return ret;
273 }
274
275 /* atts from parent */
276 iso_node_set_uid((IsoNode*)node, parent->node.uid);
277 iso_node_set_gid((IsoNode*)node, parent->node.gid);
278 iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
279
280 /* current time */
281 iso_nowtime(&now, 0);
282 iso_node_set_atime((IsoNode*)node, now);
283 iso_node_set_ctime((IsoNode*)node, now);
284 iso_node_set_mtime((IsoNode*)node, now);
285
286 if (special) {
287 *special = node;
288 }
289
290 /* add to dir */
291 return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
292 }
293
iso_image_add_new_special(IsoImage * image,IsoDir * parent,const char * name,mode_t mode,dev_t dev,IsoSpecial ** special)294 int iso_image_add_new_special(IsoImage *image, IsoDir *parent,
295 const char *name, mode_t mode,
296 dev_t dev, IsoSpecial **special)
297 {
298 int ret;
299 char *namept;
300
301 ret = iso_image_truncate_name(image, name, &namept, 0);
302 if (ret < 0)
303 return ret;
304 ret = iso_tree_add_new_special(parent, namept, mode, dev, special);
305 return ret;
306 }
307
308 /**
309 * Add a new regular file to the iso tree. Permissions are set to 0444,
310 * owner and hidden atts are taken from parent. You can modify any of them
311 * later.
312 *
313 * @param parent
314 * the dir where the new file will be created
315 * @param name
316 * name for the new file. If a node with same name already exists on
317 * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
318 * @param stream
319 * IsoStream for the contents of the file
320 * @param file
321 * place where to store a pointer to the newly created file. No extra
322 * ref is added, so you will need to call iso_node_ref() if you really
323 * need it. You can pass NULL in this parameter if you don't need the
324 * pointer
325 * @return
326 * number of nodes in parent if success, < 0 otherwise
327 * Possible errors:
328 * ISO_NULL_POINTER, if parent, name or dest are NULL
329 * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
330 * ISO_OUT_OF_MEM
331 *
332 * @since 0.6.4
333 */
iso_tree_add_new_file(IsoDir * parent,const char * name,IsoStream * stream,IsoFile ** file)334 int iso_tree_add_new_file(IsoDir *parent, const char *name, IsoStream *stream,
335 IsoFile **file)
336 {
337 int ret;
338 char *n;
339 IsoFile *node;
340 IsoNode **pos;
341 time_t now;
342
343 if (parent == NULL || name == NULL || stream == NULL) {
344 return ISO_NULL_POINTER;
345 }
346 if (file) {
347 *file = NULL;
348 }
349
350 /* find place where to insert */
351 if (iso_dir_exists(parent, name, &pos)) {
352 /* a node with same name already exists */
353 return ISO_NODE_NAME_NOT_UNIQUE;
354 }
355
356 n = strdup(name);
357 ret = iso_node_new_file(n, stream, &node);
358 if (ret < 0) {
359 free(n);
360 return ret;
361 }
362
363 /* permissions from parent */
364 iso_node_set_permissions((IsoNode*)node, 0444);
365 iso_node_set_uid((IsoNode*)node, parent->node.uid);
366 iso_node_set_gid((IsoNode*)node, parent->node.gid);
367 iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
368
369 /* current time */
370 iso_nowtime(&now, 0);
371 iso_node_set_atime((IsoNode*)node, now);
372 iso_node_set_ctime((IsoNode*)node, now);
373 iso_node_set_mtime((IsoNode*)node, now);
374
375 if (file) {
376 *file = node;
377 }
378
379 /* add to dir */
380 return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
381 }
382
iso_image_add_new_file(IsoImage * image,IsoDir * parent,const char * name,IsoStream * stream,IsoFile ** file)383 int iso_image_add_new_file(IsoImage *image, IsoDir *parent, const char *name,
384 IsoStream *stream, IsoFile **file)
385 {
386 int ret;
387 char *namept;
388
389 ret = iso_image_truncate_name(image, name, &namept, 0);
390 if (ret < 0)
391 return ret;
392 ret = iso_tree_add_new_file(parent, namept, stream, file);
393 return ret;
394 }
395
396 /**
397 * Set whether to follow or not symbolic links when added a file from a source
398 * to IsoImage.
399 */
iso_tree_set_follow_symlinks(IsoImage * image,int follow)400 void iso_tree_set_follow_symlinks(IsoImage *image, int follow)
401 {
402 image->follow_symlinks = follow ? 1 : 0;
403 }
404
405 /**
406 * Get current setting for follow_symlinks.
407 *
408 * @see iso_tree_set_follow_symlinks
409 */
iso_tree_get_follow_symlinks(IsoImage * image)410 int iso_tree_get_follow_symlinks(IsoImage *image)
411 {
412 return image->follow_symlinks;
413 }
414
415 /**
416 * Set whether to skip or not hidden files when adding a directory recursibely.
417 * Default behavior is to not ignore them, i.e., to add hidden files to image.
418 */
iso_tree_set_ignore_hidden(IsoImage * image,int skip)419 void iso_tree_set_ignore_hidden(IsoImage *image, int skip)
420 {
421 image->ignore_hidden = skip ? 1 : 0;
422 }
423
424 /**
425 * Get current setting for ignore_hidden.
426 *
427 * @see iso_tree_set_ignore_hidden
428 */
iso_tree_get_ignore_hidden(IsoImage * image)429 int iso_tree_get_ignore_hidden(IsoImage *image)
430 {
431 return image->ignore_hidden;
432 }
433
iso_tree_set_replace_mode(IsoImage * image,enum iso_replace_mode mode)434 void iso_tree_set_replace_mode(IsoImage *image, enum iso_replace_mode mode)
435 {
436 image->replace = mode;
437 }
438
iso_tree_get_replace_mode(IsoImage * image)439 enum iso_replace_mode iso_tree_get_replace_mode(IsoImage *image)
440 {
441 return image->replace;
442 }
443
444 /**
445 * Set whether to skip or not special files. Default behavior is to not skip
446 * them. Note that, despite of this setting, special files won't never be added
447 * to an image unless RR extensions were enabled.
448 *
449 * @param skip
450 * Bitmask to determine what kind of special files will be skipped:
451 * bit0: ignore FIFOs
452 * bit1: ignore Sockets
453 * bit2: ignore char devices
454 * bit3: ignore block devices
455 */
iso_tree_set_ignore_special(IsoImage * image,int skip)456 void iso_tree_set_ignore_special(IsoImage *image, int skip)
457 {
458 image->ignore_special = skip & 0x0F;
459 }
460
461 /**
462 * Get current setting for ignore_special.
463 *
464 * @see iso_tree_set_ignore_special
465 */
iso_tree_get_ignore_special(IsoImage * image)466 int iso_tree_get_ignore_special(IsoImage *image)
467 {
468 return image->ignore_special;
469 }
470
471 /**
472 * Set a callback function that libisofs will call for each file that is
473 * added to the given image by a recursive addition function. This includes
474 * image import.
475 *
476 * @param report
477 * pointer to a function that will be called just before a file will be
478 * added to the image. You can control whether the file will be in fact
479 * added or ignored.
480 * This function should return 1 to add the file, 0 to ignore it and
481 * continue, < 0 to abort the process
482 * NULL is allowed if you don't want any callback.
483 */
iso_tree_set_report_callback(IsoImage * image,int (* report)(IsoImage *,IsoFileSource *))484 void iso_tree_set_report_callback(IsoImage *image,
485 int (*report)(IsoImage*, IsoFileSource*))
486 {
487 image->report = report;
488 }
489
490 /**
491 * Add a excluded path. These are paths that won't never added to image,
492 * and will be excluded even when adding recursively its parent directory.
493 *
494 * For example, in
495 *
496 * iso_tree_add_exclude(image, "/home/user/data/private");
497 * iso_tree_add_dir_rec(image, root, "/home/user/data");
498 *
499 * the directory /home/user/data/private won't be added to image.
500 *
501 * @return
502 * 1 on success, < 0 on error
503 */
iso_tree_add_exclude(IsoImage * image,const char * path)504 int iso_tree_add_exclude(IsoImage *image, const char *path)
505 {
506 if (image == NULL || path == NULL) {
507 return ISO_NULL_POINTER;
508 }
509 image->excludes = realloc(image->excludes, ++image->nexcludes *
510 sizeof(void*));
511 if (image->excludes == NULL) {
512 return ISO_OUT_OF_MEM;
513 }
514 image->excludes[image->nexcludes - 1] = strdup(path);
515 if (image->excludes[image->nexcludes - 1] == NULL) {
516 return ISO_OUT_OF_MEM;
517 }
518 return ISO_SUCCESS;
519 }
520
521 /**
522 * Remove a previously added exclude.
523 *
524 * @see iso_tree_add_exclude
525 * @return
526 * 1 on success, 0 exclude do not exists, < 0 on error
527 */
iso_tree_remove_exclude(IsoImage * image,const char * path)528 int iso_tree_remove_exclude(IsoImage *image, const char *path)
529 {
530 size_t i, j;
531
532 if (image == NULL || path == NULL) {
533 return ISO_NULL_POINTER;
534 }
535
536 for (i = 0; (int) i < image->nexcludes; ++i) {
537 if (strcmp(image->excludes[i], path) == 0) {
538 /* exclude found */
539 free(image->excludes[i]);
540 --image->nexcludes;
541 for (j = i; (int) j < image->nexcludes; ++j) {
542 image->excludes[j] = image->excludes[j+1];
543 }
544 image->excludes = realloc(image->excludes, image->nexcludes *
545 sizeof(void*));
546 return ISO_SUCCESS;
547 }
548 }
549 return 0;
550 }
551
552 static
iso_tree_add_node_builder(IsoImage * image,IsoDir * parent,IsoFileSource * src,IsoNodeBuilder * builder,IsoNode ** node)553 int iso_tree_add_node_builder(IsoImage *image, IsoDir *parent,
554 IsoFileSource *src, IsoNodeBuilder *builder,
555 IsoNode **node)
556 {
557 int result;
558 IsoNode *new;
559 IsoNode **pos;
560 char *name = NULL, *namept;
561
562 if (parent == NULL || src == NULL || builder == NULL) {
563 result = ISO_NULL_POINTER; goto ex;
564 }
565 if (node) {
566 *node = NULL;
567 }
568
569 name = iso_file_source_get_name(src);
570
571 result = iso_image_truncate_name(image, name, &namept, 0);
572 if (result < 0)
573 return result;
574
575 /* find place where to insert */
576 result = iso_dir_exists(parent, namept, &pos);
577 if (result) {
578 /* a node with same name already exists */
579 result = ISO_NODE_NAME_NOT_UNIQUE; goto ex;
580 }
581
582 result = builder->create_node(builder, image, src, namept, &new);
583 if (result < 0)
584 goto ex;
585
586 if (node) {
587 *node = new;
588 }
589
590 /* finally, add node to parent */
591 result = iso_dir_insert(parent, (IsoNode*)new, pos, ISO_REPLACE_NEVER);
592 ex:
593 if (name != NULL)
594 free(name);
595 return result;
596 }
597
iso_tree_add_node(IsoImage * image,IsoDir * parent,const char * path,IsoNode ** node)598 int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path,
599 IsoNode **node)
600 {
601 int result;
602 IsoFilesystem *fs;
603 IsoFileSource *file;
604
605 if (image == NULL || parent == NULL || path == NULL) {
606 return ISO_NULL_POINTER;
607 }
608
609 fs = image->fs;
610 result = fs->get_by_path(fs, path, &file);
611 if (result < 0) {
612 return result;
613 }
614 result = iso_tree_add_node_builder(image, parent, file, image->builder,
615 node);
616 /* free the file */
617 iso_file_source_unref(file);
618 return result;
619 }
620
iso_tree_add_new_node(IsoImage * image,IsoDir * parent,const char * name,const char * path,IsoNode ** node)621 int iso_tree_add_new_node(IsoImage *image, IsoDir *parent, const char *name,
622 const char *path, IsoNode **node)
623 {
624 int result;
625 IsoFilesystem *fs;
626 IsoFileSource *file;
627 IsoNode *new;
628 IsoNode **pos;
629 char *namept;
630
631 if (image == NULL || parent == NULL || name == NULL || path == NULL) {
632 return ISO_NULL_POINTER;
633 }
634
635 if (node) {
636 *node = NULL;
637 }
638
639 result = iso_image_truncate_name(image, name, &namept, 0);
640 if (result < 0)
641 return result;
642
643 /* find place where to insert */
644 result = iso_dir_exists(parent, namept, &pos);
645 if (result) {
646 /* a node with same name already exists */
647 return ISO_NODE_NAME_NOT_UNIQUE;
648 }
649
650 fs = image->fs;
651 result = fs->get_by_path(fs, path, &file);
652 if (result < 0) {
653 return result;
654 }
655
656 result = image->builder->create_node(image->builder, image, file,
657 namept, &new);
658
659 /* free the file */
660 iso_file_source_unref(file);
661
662 if (result < 0) {
663 return result;
664 }
665
666 if (node) {
667 *node = new;
668 }
669
670 /* finally, add node to parent */
671 return iso_dir_insert(parent, new, pos, ISO_REPLACE_NEVER);
672 }
673
iso_tree_add_new_cut_out_node(IsoImage * image,IsoDir * parent,const char * name,const char * path,off_t offset,off_t size,IsoNode ** node)674 int iso_tree_add_new_cut_out_node(IsoImage *image, IsoDir *parent,
675 const char *name, const char *path,
676 off_t offset, off_t size,
677 IsoNode **node)
678 {
679 int result;
680 struct stat info;
681 IsoFilesystem *fs;
682 IsoFileSource *src;
683 IsoFile *new;
684 IsoNode **pos;
685 IsoStream *stream;
686 char *namept;
687
688 if (image == NULL || parent == NULL || name == NULL || path == NULL) {
689 return ISO_NULL_POINTER;
690 }
691
692 if (node) {
693 *node = NULL;
694 }
695
696 result = iso_image_truncate_name(image, name, &namept, 0);
697 if (result < 0)
698 return result;
699
700 /* find place where to insert */
701 result = iso_dir_exists(parent, namept, &pos);
702 if (result) {
703 /* a node with same name already exists */
704 return ISO_NODE_NAME_NOT_UNIQUE;
705 }
706
707 fs = image->fs;
708 result = fs->get_by_path(fs, path, &src);
709 if (result < 0) {
710 return result;
711 }
712
713 result = iso_file_source_stat(src, &info);
714 if (result < 0) {
715 iso_file_source_unref(src);
716 return result;
717 }
718 if (!S_ISREG(info.st_mode)) {
719 return ISO_WRONG_ARG_VALUE;
720 }
721 if (offset >= info.st_size) {
722 return ISO_WRONG_ARG_VALUE;
723 }
724
725 /* force regular file */
726 result = image->builder->create_file(image->builder, image, src, &new);
727
728 /* free the file */
729 iso_file_source_unref(src);
730
731 if (result < 0) {
732 return result;
733 }
734
735 /* replace file iso stream with a cut-out-stream */
736 result = iso_cut_out_stream_new(src, offset, size, &stream);
737 if (result < 0) {
738 iso_node_unref((IsoNode*)new);
739 return result;
740 }
741 iso_stream_unref(new->stream);
742 new->stream = stream;
743
744 result = iso_node_set_name((IsoNode*)new, namept);
745 if (result < 0) {
746 iso_node_unref((IsoNode*)new);
747 return result;
748 }
749
750 if (node) {
751 *node = (IsoNode*)new;
752 }
753
754 /* finally, add node to parent */
755 return iso_dir_insert(parent, (IsoNode*)new, pos, ISO_REPLACE_NEVER);
756 }
757
758 static
check_excludes(IsoImage * image,const char * path)759 int check_excludes(IsoImage *image, const char *path)
760 {
761 int i;
762
763 for (i = 0; i < image->nexcludes; ++i) {
764 char *exclude = image->excludes[i];
765 if (exclude[0] == '/') {
766 /* absolute exclude, must completely match path */
767 if (!fnmatch(exclude, path, FNM_PERIOD|FNM_PATHNAME)) {
768 return 1;
769 }
770 } else {
771 /* relative exclude, it is enough if a part of the path matches */
772 char *pos = (char*)path;
773 while (pos != NULL) {
774 pos++;
775 if (!fnmatch(exclude, pos, FNM_PERIOD|FNM_PATHNAME)) {
776 return 1;
777 }
778 pos = strchr(pos, '/');
779 }
780 }
781 }
782 return 0;
783 }
784
785 static
check_hidden(IsoImage * image,const char * name)786 int check_hidden(IsoImage *image, const char *name)
787 {
788 return (image->ignore_hidden && name[0] == '.');
789 }
790
791 static
check_special(IsoImage * image,mode_t mode)792 int check_special(IsoImage *image, mode_t mode)
793 {
794 if (image->ignore_special != 0) {
795 switch(mode & S_IFMT) {
796 case S_IFBLK:
797 return image->ignore_special & 0x08 ? 1 : 0;
798 case S_IFCHR:
799 return image->ignore_special & 0x04 ? 1 : 0;
800 case S_IFSOCK:
801 return image->ignore_special & 0x02 ? 1 : 0;
802 case S_IFIFO:
803 return image->ignore_special & 0x01 ? 1 : 0;
804 default:
805 return 0;
806 }
807 }
808 return 0;
809 }
810
811
812 static
ascii_increment(char * name,int len,int pos,int rollover_carry)813 void ascii_increment(char *name, int len, int pos, int rollover_carry)
814 {
815 int c;
816
817 again:;
818 if (pos < 0 || pos >= len)
819 pos = len - 1;
820 c = name[pos];
821 if (c >= '0' && c < '9') {
822 c++;
823 } else if (c == '9') {
824 c = 'A';
825 } else if (c >= 'A' && c < 'Z') {
826 c++;
827 } else if (c == 'Z') {
828 c = '_';
829 } else if (c == '_') {
830 c = 'a';
831 } else if (c >= 'a' && c < 'z') {
832 c++;
833 } else if (c == 'z') {
834 c = '0';
835 name[pos] = c;
836 pos--;
837 if (pos >= 0 || rollover_carry)
838 goto again;
839 return;
840 } else {
841 if (pos == len - 1 || name[pos + 1] == '.')
842 c = '_'; /* Make first change less riddling */
843 else
844 c = '0'; /* But else use the full range of valid characters */
845 }
846 name[pos] = c;
847 }
848
849 static
insert_underscores(char * name,int * len,int * at_pos,int count,char ** new_name)850 int insert_underscores(char *name, int *len, int *at_pos, int count,
851 char **new_name)
852 {
853 int ret;
854
855 LIBISO_ALLOC_MEM(*new_name, char, count + *len + 1);
856 if (*at_pos > 0)
857 memcpy(*new_name, name, *at_pos);
858 if (count > 0)
859 memset(*new_name + *at_pos, '_', count);
860 if (*len > *at_pos)
861 memcpy(*new_name + *at_pos + count, name + *at_pos, *len - *at_pos);
862 (*new_name)[count + *len] = 0;
863 *len += count;
864 *at_pos += count;
865 ret= ISO_SUCCESS;
866 ex:;
867 return ret;
868 }
869
870 static
make_incrementable_name(char ** name,char ** unique_name,int * low_pos,int * rollover_carry,int * pre_check)871 int make_incrementable_name(char **name, char **unique_name, int *low_pos,
872 int *rollover_carry, int *pre_check)
873 {
874 char *dpt, *npt;
875 int first, len, ret;
876
877 /* The incrementable part of the file shall have at least 7 characters.
878 There may be up to pow(2.0,32.0)*2048/33 = 266548273400 files.
879 The set of increment result characters has 63 elements.
880 pow(63.0,7.0) is nearly 15 times larger than 266548273400.
881 */
882 static int min_incr = 7;
883
884 /* At most two suffixes of total length up to 12, like .tar.bz2,
885 shall be preserved. The incrementable part will eventually be
886 padded up.
887 Incrementing begins before the last suffix in any case. But when this
888 rolls over on short prefixes, then long last suffixes will get used
889 as high characters of the incremental part. This is indicated by
890 *rollover_carry which corresponds to the parameter of ascii_increment()
891 with the same name.
892 */
893 static int max_suffix = 12;
894
895 *rollover_carry = 0;
896 *pre_check = 0;
897
898 len = strlen(*name);
899
900 /* Check if the part before the first dot is long enough.
901 If not, then preserve the last two short suffixes.
902 */
903 dpt = strchr(*name, '.');
904 if (dpt != NULL)
905 if ((dpt - *name) < min_incr)
906 dpt = strrchr(*name, '.');
907 if (dpt != NULL) {
908 first= (dpt - *name);
909 if (dpt > *name && len - first < max_suffix) {
910 for(npt = dpt - 1; npt >= *name && *npt != '.'; npt--);
911 if (npt >= *name) {
912 if (len - (npt - *name) <= max_suffix) {
913 first= (npt - *name);
914 dpt = npt;
915 }
916 }
917 }
918 } else
919 first= len;
920 if (first < min_incr && (len - first) <= max_suffix) {
921 ret = insert_underscores(*name, &len, &first, min_incr - first,
922 unique_name);
923 if (ret < 0)
924 goto ex;
925 *pre_check = 1; /* It might now already be unique */
926
927 } else if (len < 64) {
928 /* Insert an underscore to preserve the original name at least for the
929 first few increments
930 */
931 ret = insert_underscores(*name, &len, &first, 1, unique_name);
932 if (ret < 0)
933 goto ex;
934 *pre_check = 1;
935
936 } else {
937 LIBISO_ALLOC_MEM(*unique_name, char, len + 1);
938 memcpy(*unique_name, *name, len);
939 if (first < min_incr)
940 *rollover_carry = 1; /* Do not get caged before the dots */
941 }
942 (*unique_name)[len] = 0;
943 *low_pos = first - 1;
944 ret = 1;
945 ex:;
946 return(ret);
947 }
948
949 static
make_really_unique_name(IsoDir * parent,char ** name,char ** unique_name,IsoNode *** pos,int flag)950 int make_really_unique_name(IsoDir *parent, char **name, char **unique_name,
951 IsoNode ***pos, int flag)
952 {
953 int ret, rollover_carry = 0, pre_check = 0, ascii_idx = -1, len;
954
955 ret = make_incrementable_name(name, unique_name, &ascii_idx,
956 &rollover_carry, &pre_check);
957 if (ret < 0)
958 goto ex;
959 len = strlen(*unique_name);
960 while (1) {
961 if (!pre_check)
962 ascii_increment(*unique_name, len, ascii_idx, !!rollover_carry);
963 else
964 pre_check = 0;
965 ret = iso_dir_exists(parent, *unique_name, pos);
966 if (ret < 0)
967 goto ex;
968 if (ret == 0)
969 break;
970 }
971 *name = *unique_name;
972 ret = ISO_SUCCESS;
973 ex:;
974 if (ret < 0) {
975 LIBISO_FREE_MEM(*unique_name);
976 *unique_name = NULL;
977 }
978 return ret;
979 }
980
981 /**
982 * Recursively add a given directory to the image tree.
983 *
984 * @return
985 * 1 continue, < 0 error (ISO_CANCELED stop)
986 */
iso_add_dir_src_rec(IsoImage * image,IsoDir * parent,IsoFileSource * dir)987 int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir)
988 {
989 int ret, dir_is_open = 0;
990 IsoNodeBuilder *builder;
991 IsoFileSource *file;
992 IsoNode **pos;
993 struct stat info;
994 char *name, *path, *allocated_name = NULL;
995 IsoNode *new;
996 enum iso_replace_mode replace;
997
998 ret = iso_file_source_open(dir);
999 if (ret < 0) {
1000 path = iso_file_source_get_path(dir);
1001 /* instead of the probable error, we throw a sorry event */
1002 if (path != NULL) {
1003 ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret,
1004 "Can't open dir %s", path);
1005 free(path);
1006 } else {
1007 ret = iso_msg_submit(image->id, ISO_NULL_POINTER, ret,
1008 "Can't open dir. NULL pointer caught as dir name");
1009 }
1010 goto ex;
1011 }
1012 dir_is_open = 1;
1013
1014 builder = image->builder;
1015
1016 /* iterate over all directory children */
1017 while (1) {
1018 int skip = 0;
1019
1020 ret = iso_file_source_readdir(dir, &file);
1021 if (ret <= 0) {
1022 if (ret < 0) {
1023 /* error reading dir */
1024 ret = iso_msg_submit(image->id, ret, ret, "Error reading dir");
1025 goto ex;
1026 }
1027 break; /* End of directory */
1028 }
1029
1030 path = iso_file_source_get_path(file);
1031 if (path == NULL) {
1032 ret = iso_msg_submit(image->id, ISO_NULL_POINTER, ret,
1033 "NULL pointer caught as file path");
1034 goto ex;
1035 }
1036 name = strrchr(path, '/') + 1;
1037
1038 if (image->follow_symlinks) {
1039 ret = iso_file_source_stat(file, &info);
1040 } else {
1041 ret = iso_file_source_lstat(file, &info);
1042 }
1043 if (ret < 0) {
1044 ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret,
1045 "Error when adding file %s", path);
1046 goto dir_rec_continue;
1047 }
1048
1049 if (check_excludes(image, path)) {
1050 iso_msg_debug(image->id, "Skipping excluded file %s", path);
1051 skip = 1;
1052 } else if (check_hidden(image, name)) {
1053 iso_msg_debug(image->id, "Skipping hidden file %s", path);
1054 skip = 1;
1055 } else if (check_special(image, info.st_mode)) {
1056 iso_msg_debug(image->id, "Skipping special file %s", path);
1057 skip = 1;
1058 }
1059
1060 if (skip) {
1061 goto dir_rec_continue;
1062 }
1063
1064 replace = image->replace;
1065
1066 /* find place where to insert */
1067 ret = iso_dir_exists(parent, name, &pos);
1068 if (ret) {
1069 /* Resolve name collision
1070 e.g. caused by fs_image.c:make_hopefully_unique_name()
1071 */
1072 LIBISO_FREE_MEM(allocated_name); allocated_name = NULL;
1073 ret = make_really_unique_name(parent, &name, &allocated_name, &pos,
1074 0);
1075 if (ret < 0)
1076 goto ex;
1077 image->collision_warnings++;
1078 if (image->collision_warnings < ISO_IMPORT_COLL_WARN_MAX) {
1079 ret = iso_msg_submit(image->id, ISO_IMPORT_COLLISION, 0,
1080 "File name collision resolved with %s . Now: %s",
1081 path, name);
1082 if (ret < 0)
1083 goto ex;
1084 }
1085 }
1086
1087 /* if we are here we must insert. Give user a chance for cancel */
1088 if (image->report) {
1089 int r = image->report(image, file);
1090 if (r <= 0) {
1091 ret = (r < 0 ? ISO_CANCELED : ISO_SUCCESS);
1092 goto dir_rec_continue;
1093 }
1094 }
1095 ret = builder->create_node(builder, image, file, name, &new);
1096 if (ret < 0) {
1097 ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret,
1098 "Error when adding file %s", path);
1099 goto dir_rec_continue;
1100 }
1101
1102 /* ok, node has correctly created, we need to add it */
1103 ret = iso_dir_insert(parent, new, pos, replace);
1104 if (ret < 0) {
1105 iso_node_unref(new);
1106 if (ret != (int) ISO_NODE_NAME_NOT_UNIQUE) {
1107 /* error */
1108 goto dir_rec_continue;
1109 } else {
1110 /* file ignored because a file with same node already exists */
1111 iso_msg_debug(image->id, "Skipping file %s. A node with same "
1112 "file already exists", path);
1113 ret = 0;
1114 }
1115 } else {
1116 iso_msg_debug(image->id, "Added file %s", path);
1117 }
1118
1119 /* finally, if the node is a directory we need to recurse */
1120 if (new->type == LIBISO_DIR && S_ISDIR(info.st_mode)) {
1121 ret = iso_add_dir_src_rec(image, (IsoDir*)new, file);
1122 }
1123
1124 dir_rec_continue:;
1125 free(path);
1126 iso_file_source_unref(file);
1127
1128 /* check for error severity to decide what to do */
1129 if (ret < 0) {
1130 ret = iso_msg_submit(image->id, ret, 0, NULL);
1131 if (ret < 0)
1132 goto ex;
1133 }
1134 } /* while */
1135
1136 ret = ISO_SUCCESS;
1137 ex:;
1138 if (dir_is_open)
1139 iso_file_source_close(dir);
1140 LIBISO_FREE_MEM(allocated_name);
1141 return ret;
1142 }
1143
iso_tree_add_dir_rec(IsoImage * image,IsoDir * parent,const char * dir)1144 int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir)
1145 {
1146 int result;
1147 struct stat info;
1148 IsoFilesystem *fs;
1149 IsoFileSource *file;
1150
1151 if (image == NULL || parent == NULL || dir == NULL) {
1152 return ISO_NULL_POINTER;
1153 }
1154
1155 fs = image->fs;
1156 result = fs->get_by_path(fs, dir, &file);
1157 if (result < 0) {
1158 return result;
1159 }
1160
1161 /* we also allow dir path to be a symlink to a dir */
1162 result = iso_file_source_stat(file, &info);
1163 if (result < 0) {
1164 iso_file_source_unref(file);
1165 return result;
1166 }
1167
1168 if (!S_ISDIR(info.st_mode)) {
1169 iso_file_source_unref(file);
1170 return ISO_FILE_IS_NOT_DIR;
1171 }
1172 result = iso_add_dir_src_rec(image, parent, file);
1173 iso_file_source_unref(file);
1174 return result;
1175 }
1176
1177 /* @param flag bit0= truncate according to image truncate mode and length
1178 */
iso_tree_path_to_node_flag(IsoImage * image,const char * path,IsoNode ** node,int flag)1179 int iso_tree_path_to_node_flag(IsoImage *image, const char *path,
1180 IsoNode **node, int flag)
1181 {
1182 int result;
1183 IsoNode *n;
1184 IsoDir *dir;
1185 char *ptr, *brk_info = NULL, *component;
1186
1187 if (image == NULL || path == NULL) {
1188 return ISO_NULL_POINTER;
1189 }
1190
1191 /* get the first child at the root of the image that is "/" */
1192 dir = image->root;
1193 n = (IsoNode *)dir;
1194 if (!strcmp(path, "/")) {
1195 if (node) {
1196 *node = n;
1197 }
1198 return ISO_SUCCESS;
1199 }
1200
1201 ptr = strdup(path);
1202 if (ptr == NULL)
1203 return ISO_OUT_OF_MEM;
1204 result = 0;
1205
1206 /* get the first component of the path */
1207 component = strtok_r(ptr, "/", &brk_info);
1208 while (component) {
1209 if (n->type != LIBISO_DIR) {
1210 n = NULL;
1211 result = 0;
1212 break;
1213 }
1214 dir = (IsoDir *)n;
1215
1216 if ((flag & 1) && image->truncate_mode == 1) {
1217 result = iso_dir_get_node_trunc(dir, image->truncate_length,
1218 component, &n);
1219 } else {
1220 result = iso_dir_get_node(dir, component, &n);
1221 }
1222 if (result != 1) {
1223 n = NULL;
1224 break;
1225 }
1226
1227 component = strtok_r(NULL, "/", &brk_info);
1228 }
1229
1230 free(ptr);
1231 if (node) {
1232 *node = n;
1233 }
1234 return result;
1235 }
1236
iso_tree_path_to_node(IsoImage * image,const char * path,IsoNode ** node)1237 int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node)
1238 {
1239 return iso_tree_path_to_node_flag(image, path, node, 0);
1240 }
1241
iso_image_path_to_node(IsoImage * image,const char * path,IsoNode ** node)1242 int iso_image_path_to_node(IsoImage *image, const char *path, IsoNode **node)
1243 {
1244 return iso_tree_path_to_node_flag(image, path, node, 1);
1245 }
1246
iso_tree_get_node_path(IsoNode * node)1247 char *iso_tree_get_node_path(IsoNode *node)
1248 {
1249 char *path = NULL, *parent_path = NULL;
1250
1251 if (node == NULL || node->parent == NULL)
1252 return NULL;
1253
1254 if ((IsoNode*)node->parent == node) {
1255 return strdup("/");
1256 } else {
1257 parent_path = iso_tree_get_node_path((IsoNode*)node->parent);
1258 if (parent_path == NULL)
1259 goto ex;
1260 if (strlen(parent_path) == 1) {
1261 path = calloc(1, strlen(node->name) + 2);
1262 if (path == NULL)
1263 goto ex;
1264 sprintf(path, "/%s", node->name);
1265 } else {
1266 path = calloc(1, strlen(parent_path) + strlen(node->name) + 2);
1267 if (path == NULL)
1268 goto ex;
1269 sprintf(path, "%s/%s", parent_path, node->name);
1270 }
1271 }
1272 ex:;
1273 if (parent_path != NULL)
1274 free(parent_path);
1275 return path;
1276 }
1277
1278 /* Note: No reference is taken to the found node.
1279 @param flag bit0= recursion
1280 */
iso_tree_get_node_of_block(IsoImage * image,IsoDir * dir,uint32_t block,IsoNode ** found,uint32_t * next_above,int flag)1281 int iso_tree_get_node_of_block(IsoImage *image, IsoDir *dir, uint32_t block,
1282 IsoNode **found, uint32_t *next_above, int flag)
1283 {
1284 int ret, section_count, i;
1285 IsoDirIter *iter = NULL;
1286 IsoNode *node;
1287 IsoDir *subdir;
1288 IsoFile *file;
1289 struct iso_file_section *sections = NULL;
1290 uint32_t na = 0;
1291
1292 if (dir == NULL)
1293 dir = image->root;
1294
1295 ret = iso_dir_get_children(dir, &iter);
1296 while (iso_dir_iter_next(iter, &node) == 1 ) {
1297
1298 if (ISO_NODE_IS_FILE(node)) {
1299 file = (IsoFile *) node;
1300 ret = iso_file_get_old_image_sections(file, §ion_count,
1301 §ions, 0);
1302 if (ret <= 0)
1303 continue;
1304 for (i = 0; i < section_count; i++) {
1305 if (sections[i].block <= block &&
1306 block - sections[i].block <
1307 (((off_t) sections[i].size) + 2047) / 2048) {
1308 *found = node;
1309 ret = 1; goto ex;
1310 }
1311 if ((na == 0 || sections[i].block < na) &&
1312 sections[i].block > block)
1313 na = sections[i].block;
1314 }
1315 free(sections); sections = NULL;
1316 } else if (ISO_NODE_IS_DIR(node)) {
1317 subdir = (IsoDir *) node;
1318 ret = iso_tree_get_node_of_block(image, subdir, block, found, &na,
1319 1);
1320 if (ret != 0)
1321 goto ex;
1322 }
1323 }
1324 if (next_above != NULL && (na > 0 || !(flag & 1)))
1325 if (*next_above == 0 || *next_above > na || !(flag & 1))
1326 *next_above = na;
1327 ret = 0;
1328 ex:
1329 if (sections != NULL)
1330 free(sections);
1331 if (iter != NULL)
1332 iso_dir_iter_free(iter);
1333 return ret;
1334 }
1335
1336
1337 /* ------------------------- tree cloning ------------------------------ */
1338
1339 static
iso_tree_copy_node_attr(IsoNode * old_node,IsoNode * new_node,int flag)1340 int iso_tree_copy_node_attr(IsoNode *old_node, IsoNode *new_node, int flag)
1341 {
1342 int ret;
1343
1344 new_node->mode = old_node->mode;
1345 new_node->uid = old_node->uid;
1346 new_node->gid = old_node->gid;
1347 new_node->atime = old_node->atime;
1348 new_node->mtime = old_node->mtime;
1349 new_node->ctime = old_node->ctime;
1350 new_node->hidden = old_node->hidden;
1351 ret = iso_node_clone_xinfo(old_node, new_node, 0);
1352 if (ret < 0)
1353 return ret;
1354 return ISO_SUCCESS;
1355 }
1356
1357 /*
1358 @param flag bit0= merge directory with *new_node
1359 */
1360 static
iso_tree_clone_dir(IsoDir * old_dir,IsoDir * new_parent,char * new_name,IsoNode ** new_node,int flag)1361 int iso_tree_clone_dir(IsoDir *old_dir,
1362 IsoDir *new_parent, char *new_name, IsoNode **new_node,
1363 int flag)
1364 {
1365 IsoDir *new_dir = NULL;
1366 IsoNode *sub_node = NULL, *new_sub_node = NULL;
1367 IsoDirIter *iter = NULL;
1368 int ret;
1369
1370 if (flag & 1) {
1371 new_dir = (IsoDir *) *new_node;
1372 } else {
1373 *new_node = NULL;
1374 ret = iso_tree_add_new_dir(new_parent, new_name, &new_dir);
1375 if (ret < 0)
1376 return ret;
1377 }
1378 /* Avoid traversal of target directory to allow cloning of old_dir to a
1379 subordinate of old_dir.
1380 */
1381 iso_node_take((IsoNode *) new_dir);
1382
1383 ret = iso_dir_get_children(old_dir, &iter);
1384 if (ret < 0)
1385 goto ex;
1386 while(1) {
1387 ret = iso_dir_iter_next(iter, &sub_node);
1388 if (ret == 0)
1389 break;
1390 ret = iso_tree_clone(sub_node, new_dir, sub_node->name, &new_sub_node,
1391 flag & 1);
1392 if (ret < 0)
1393 goto ex;
1394 }
1395
1396 /* Now graft in the new tree resp. graft back the merged tree */
1397 ret = iso_dir_add_node(new_parent, (IsoNode *) new_dir, 0);
1398 if (ret < 0)
1399 goto ex;
1400
1401 if (!(flag & 1))
1402 *new_node = (IsoNode *) new_dir;
1403 ret = ISO_SUCCESS;
1404 ex:;
1405 if (iter != NULL)
1406 iso_dir_iter_free(iter);
1407 if (ret < 0 && new_dir != NULL) {
1408 if (flag & 1) {
1409 /* graft back the merged tree (eventually with half copy) */
1410 iso_dir_add_node(new_parent, (IsoNode *) new_dir, 0);
1411 } else {
1412 iso_node_remove_tree((IsoNode *) new_dir, NULL);
1413 *new_node = NULL;
1414 }
1415 }
1416 return ret;
1417 }
1418
1419 static
iso_tree_clone_file(IsoFile * old_file,IsoDir * new_parent,char * new_name,IsoNode ** new_node,int flag)1420 int iso_tree_clone_file(IsoFile *old_file,
1421 IsoDir *new_parent, char *new_name, IsoNode **new_node,
1422 int flag)
1423 {
1424 IsoStream *new_stream = NULL;
1425 IsoFile *new_file = NULL;
1426 int ret;
1427
1428 *new_node = NULL;
1429
1430 ret = iso_stream_clone(old_file->stream, &new_stream, 0);
1431 if (ret < 0)
1432 return ret;
1433
1434 ret = iso_tree_add_new_file(new_parent, new_name, new_stream, &new_file);
1435 if (ret < 0)
1436 goto ex;
1437 new_stream = NULL; /* now owned by new_file */
1438 new_file->sort_weight = old_file->sort_weight;
1439 *new_node = (IsoNode *) new_file;
1440 ret = ISO_SUCCESS;
1441 ex:;
1442 if (new_stream != NULL)
1443 iso_stream_unref(new_stream);
1444 return ret;
1445 }
1446
1447 static
iso_tree_clone_symlink(IsoSymlink * node,IsoDir * new_parent,char * new_name,IsoNode ** new_node,int flag)1448 int iso_tree_clone_symlink(IsoSymlink *node,
1449 IsoDir *new_parent, char *new_name, IsoNode **new_node,
1450 int flag)
1451 {
1452 IsoSymlink *new_sym;
1453 int ret;
1454
1455 *new_node = NULL;
1456
1457 ret = iso_tree_add_new_symlink(new_parent, new_name, node->dest, &new_sym);
1458 if (ret < 0)
1459 return ret;
1460 new_sym->fs_id = node->fs_id;
1461 new_sym->st_dev = node->st_dev;
1462 new_sym->st_ino = node->st_ino;
1463 *new_node = (IsoNode *) new_sym;
1464 return ISO_SUCCESS;
1465 }
1466
1467 static
iso_tree_clone_special(IsoSpecial * node,IsoDir * new_parent,char * new_name,IsoNode ** new_node,int flag)1468 int iso_tree_clone_special(IsoSpecial *node,
1469 IsoDir *new_parent, char *new_name, IsoNode **new_node,
1470 int flag)
1471 {
1472 IsoSpecial *new_spec;
1473 IsoNode *iso_node;
1474 int ret;
1475
1476 iso_node = (IsoNode *) node;
1477 ret = iso_tree_add_new_special(new_parent, new_name, iso_node->mode,
1478 node->dev, &new_spec);
1479 if (ret < 0)
1480 return ret;
1481 new_spec->fs_id = node->fs_id;
1482 new_spec->st_dev = node->st_dev;
1483 new_spec->st_ino = node->st_ino;
1484 *new_node = (IsoNode *) new_spec;
1485 return ISO_SUCCESS;
1486 }
1487
1488
1489 /* @param flag bit0= Merge directories rather than ISO_NODE_NAME_NOT_UNIQUE.
1490 bit1= issue warning in case of truncation
1491 */
iso_tree_clone_trunc(IsoNode * node,IsoDir * new_parent,char * new_name_in,IsoNode ** new_node,int truncate_length,int flag)1492 int iso_tree_clone_trunc(IsoNode *node, IsoDir *new_parent,
1493 char *new_name_in, IsoNode **new_node,
1494 int truncate_length, int flag)
1495 {
1496 int ret = ISO_SUCCESS;
1497 char *new_name, *trunc = NULL;
1498
1499 *new_node = NULL;
1500 new_name = new_name_in;
1501 if (truncate_length >= 64 && (int) strlen(new_name) > truncate_length) {
1502 trunc = strdup(new_name);
1503 if (trunc == 0) {
1504 ret = ISO_OUT_OF_MEM;
1505 goto ex;
1506 }
1507 ret = iso_truncate_rr_name(1, truncate_length, trunc, !(flag & 2));
1508 if (ret < 0)
1509 goto ex;
1510 new_name = trunc;
1511 }
1512 if (iso_dir_get_node(new_parent, new_name, new_node) == 1) {
1513 if (! (node->type == LIBISO_DIR && (*new_node)->type == LIBISO_DIR &&
1514 (flag & 1))) {
1515 *new_node = NULL;
1516 ret = ISO_NODE_NAME_NOT_UNIQUE;
1517 goto ex;
1518 }
1519 } else
1520 flag &= ~1;
1521
1522 if (node->type == LIBISO_DIR) {
1523 ret = iso_tree_clone_dir((IsoDir *) node, new_parent, new_name,
1524 new_node, flag & 1);
1525 } else if (node->type == LIBISO_FILE) {
1526 ret = iso_tree_clone_file((IsoFile *) node, new_parent, new_name,
1527 new_node, 0);
1528 } else if (node->type == LIBISO_SYMLINK) {
1529 ret = iso_tree_clone_symlink((IsoSymlink *) node, new_parent, new_name,
1530 new_node, 0);
1531 } else if (node->type == LIBISO_SPECIAL) {
1532 ret = iso_tree_clone_special((IsoSpecial *) node, new_parent, new_name,
1533 new_node, 0);
1534 } else if (node->type == LIBISO_BOOT) {
1535 ret = ISO_SUCCESS; /* API says they are silently ignored */
1536 }
1537 if (ret < 0)
1538 goto ex;
1539 if (flag & 1) {
1540 ret = 2; /* merged two directories, *new_node is not new */
1541 goto ex;
1542 }
1543 ret = iso_tree_copy_node_attr(node, *new_node, 0);
1544
1545 ex:;
1546 if (trunc != NULL)
1547 free(trunc);
1548 return ret;
1549 }
1550
1551
1552 /* API */
iso_tree_clone(IsoNode * node,IsoDir * new_parent,char * new_name,IsoNode ** new_node,int flag)1553 int iso_tree_clone(IsoNode *node,
1554 IsoDir *new_parent, char *new_name, IsoNode **new_node,
1555 int flag)
1556 {
1557 return iso_tree_clone_trunc(node, new_parent, new_name, new_node, 0,
1558 flag & 1);
1559 }
1560
1561
1562 /* API */
iso_image_tree_clone(IsoImage * image,IsoNode * node,IsoDir * new_parent,char * new_name,IsoNode ** new_node,int flag)1563 int iso_image_tree_clone(IsoImage *image, IsoNode *node, IsoDir *new_parent,
1564 char *new_name, IsoNode **new_node, int flag)
1565 {
1566 int length, ret;
1567
1568 if (image->truncate_mode == 0)
1569 length = 0;
1570 else
1571 length = image->truncate_length;
1572 ret = iso_tree_clone_trunc(node, new_parent, new_name, new_node, length,
1573 flag & 3);
1574 return ret;
1575 }
1576
1577
iso_tree_resolve_symlink(IsoImage * img,IsoSymlink * sym,IsoNode ** res,int * depth,int flag)1578 int iso_tree_resolve_symlink(IsoImage *img, IsoSymlink *sym, IsoNode **res,
1579 int *depth, int flag)
1580 {
1581 IsoDir *cur_dir = NULL;
1582 IsoNode *n, *resolved_node;
1583 char *dest, *dest_start, *dest_end;
1584 int ret = 0;
1585 unsigned int comp_len, dest_len;
1586
1587 dest = sym->dest;
1588 dest_len = strlen(dest);
1589
1590 if (dest[0] == '/') {
1591
1592 /* ??? How to resolve absolute links without knowing the
1593 path of the future mount point ?
1594 ??? Would it be better to throw error ?
1595 I can only assume that it gets mounted at / during some stage
1596 of booting.
1597 */;
1598
1599 cur_dir = img->root;
1600 dest_end = dest;
1601 } else {
1602 cur_dir = sym->node.parent;
1603 if (cur_dir == NULL)
1604 cur_dir = img->root;
1605 dest_end = dest - 1;
1606 }
1607
1608 while (dest_end < dest + dest_len) {
1609 dest_start = dest_end + 1;
1610 dest_end = strchr(dest_start, '/');
1611 if (dest_end == NULL)
1612 dest_end = dest_start + strlen(dest_start);
1613 comp_len = dest_end - dest_start;
1614 if (comp_len == 0 || (comp_len == 1 && dest_start[0] == '.'))
1615 continue;
1616 if (comp_len == 2 && dest_start[0] == '.' && dest_start[1] == '.') {
1617 cur_dir = cur_dir->node.parent;
1618 if (cur_dir == NULL) /* link shoots over root */
1619 return ISO_DEAD_SYMLINK;
1620 continue;
1621 }
1622
1623 /* Search node in cur_dir */
1624 for (n = cur_dir->children; n != NULL; n = n->next)
1625 if (strncmp(dest_start, n->name, comp_len) == 0 &&
1626 strlen(n->name) == comp_len)
1627 break;
1628 if (n == NULL)
1629 return ISO_DEAD_SYMLINK;
1630
1631 if (n->type == LIBISO_DIR) {
1632 cur_dir = (IsoDir *) n;
1633 } else if (n->type == LIBISO_SYMLINK) {
1634 if (*depth >= LIBISO_MAX_LINK_DEPTH)
1635 return ISO_DEEP_SYMLINK;
1636 (*depth)++;
1637 ret = iso_tree_resolve_symlink(img, (IsoSymlink *) n,
1638 &resolved_node, depth, 0);
1639 if (ret < 0)
1640 return ret;
1641 if (resolved_node->type != LIBISO_DIR) {
1642 n = resolved_node;
1643 goto leaf_type;
1644 }
1645 cur_dir = (IsoDir *) resolved_node;
1646 } else {
1647 leaf_type:;
1648 if (dest_end < dest + dest_len) /* attempt to dive into file */
1649 return ISO_DEAD_SYMLINK;
1650 *res = n;
1651 return ISO_SUCCESS;
1652 }
1653 }
1654 *res = (IsoNode *) cur_dir;
1655 return ISO_SUCCESS;
1656 }
1657
1658