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, &section_count,
1301                                                   &sections, 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