
/* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images.

   Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net>

   Provided under GPL version 2 or later.

   This file contains functions which manipulate the libisofs tree model.
*/

#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif

#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>


#include "xorriso.h"
#include "xorriso_private.h"
#include "xorrisoburn.h"

#include "lib_mgt.h"
#include "iso_img.h"
#include "iso_tree.h"
#include "iso_manip.h"
#include "sort_cmp.h"
#include "parse_exec.h"



/* @param flag bit0= give directory x-permission where is r-permission
               bit1= do not transfer ACL or xattr
               bit2= record dev,inode (only if enabled by xorriso)
               bit5= transfer ACL or xattr from eventual link target
*/
int Xorriso_transfer_properties(struct XorrisO *xorriso, struct stat *stbuf,
                               char *disk_path,  IsoNode *node, int flag)
{
 mode_t mode;
 int ret= 1;
 size_t num_attrs= 0, *value_lengths= NULL;
 char **names= NULL, **values= NULL;

 mode= stbuf->st_mode;

 if((!(flag & 2)) && !(xorriso->do_aaip & 1))
   /* Will drop ACL. Update mode S_IRWXG by eventual group:: ACL entry */
   iso_local_get_perms_wo_acl(disk_path, &mode, flag & 32);

 if((flag&1) && S_ISDIR(mode)) {
   if(mode&S_IRUSR)
     mode|= S_IXUSR;
   if(mode&S_IRGRP)
     mode|= S_IXGRP;
   if(mode&S_IROTH)
     mode|= S_IXOTH;
 }
 iso_node_set_permissions(node, mode & 07777);
 iso_node_set_uid(node, stbuf->st_uid);
 iso_node_set_gid(node, stbuf->st_gid);
 iso_node_set_atime(node, stbuf->st_atime);
 iso_node_set_mtime(node, stbuf->st_mtime);
 iso_node_set_ctime(node, stbuf->st_ctime);

 if((xorriso->do_aaip & 5) && !(flag & 2)) {
   ret= iso_local_get_attrs(disk_path, &num_attrs, &names, &value_lengths,
                            &values, ((xorriso->do_aaip & 1) && !(flag & 2))
                                     | ((!(xorriso->do_aaip & 4)) << 2)
                                     | (flag & 32));
   if(ret < 0) { 
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, disk_path, ret,
                              "Error when obtaining local ACL and xattr", 0,
                              "FAILURE", 1 | 2);
     ret= 0; goto ex;
   }
   ret= iso_node_set_attrs(node, num_attrs, names, value_lengths, values,
                           1 | 8);
   if(ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, "", ret,
                              "Error when setting ACL and xattr to image node",
                              0, "FAILURE", 1);
     ret= 0; goto ex;
   }
 }

 if((flag & 4) && ((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2))) {
   ret= Xorriso_record_dev_inode(xorriso, disk_path, (dev_t) 0, (ino_t) 0,
                                 (void *) node, "", flag & 32);
   if(ret <= 0)
     goto ex;
 }

 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 iso_local_get_attrs(disk_path, &num_attrs, &names, &value_lengths,
                      &values, 1 << 15); /* free memory */
 return(ret);
}


int Xorriso_graft_split(struct XorrisO *xorriso, IsoImage *volume,
                        IsoDir *dir, char *disk_path, char *img_name,
                        char *nominal_source, char *nominal_target,
                        off_t size, IsoNode **node, int flag)
{
 int ret;
 IsoDir *new_dir= NULL;
 IsoNode *part_node;
 int partno, total_parts;
 off_t offset;
 char part_name[SfileadrL], sfe[5*SfileadrL];

 ret= iso_tree_add_new_dir(dir, img_name, &new_dir);
 if(ret<0)
   return(ret);
 *node= (IsoNode *) new_dir;
 total_parts= size / xorriso->split_size;
 if(size % xorriso->split_size)
   total_parts++;
 for(partno= 1; partno<=total_parts; partno++) {
   offset = xorriso->split_size * (off_t) (partno-1);
   Splitpart__compose(part_name, partno, total_parts, offset,
                      xorriso->split_size, size, 0);
   ret= Xorriso_tree_graft_node(xorriso, volume,
                                new_dir, disk_path, part_name,
                                nominal_source, nominal_target,
                                offset, xorriso->split_size,
                                &part_node, 8);
   if(ret<=0)
     return(0);
 }
 sprintf(xorriso->info_text, "Split into %d parts: %s",
         total_parts, Text_shellsafe(nominal_target, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
 return(1);
}


/* 
   @param flag bit3= cut_out_node: offset and size are valid
               bit8= hide in iso_rr
               bit9= hide in joliet
*/
int Xorriso_tree_graft_node(struct XorrisO *xorriso, IsoImage *volume,
                            IsoDir *dir, char *disk_path, char *img_name,
                            char *nominal_source, char *nominal_target,
                            off_t offset, off_t cut_size,
                            IsoNode **node, int flag)
{
 int ret, stbuf_valid= 0;
 struct stat stbuf;
 char sfe[5*SfileadrL];
 off_t size= 0;

 if(lstat(disk_path, &stbuf) != -1) {
   stbuf_valid= 1;
   if(S_ISREG(stbuf.st_mode))
     size= stbuf.st_size;
 }
 if(flag&8)  {
   if(cut_size > xorriso->file_size_limit && xorriso->file_size_limit > 0) {
     sprintf(xorriso->info_text,
             "File piece exceeds size limit of %.f bytes: %.f from %s\n",
             (double) xorriso->file_size_limit, (double) cut_size,
             Text_shellsafe(disk_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   }
   ret= iso_tree_add_new_cut_out_node(volume, dir, img_name, disk_path,
                                      offset, cut_size, node);
   if(ret<0)
     goto ex;
 } else {
   if(xorriso->split_size > 0 && size > xorriso->split_size) {
     ret= Xorriso_graft_split(xorriso, volume, dir, disk_path, img_name,
                              nominal_source, nominal_target, size,
                              node, 0);
     if(ret<=0)
       goto ex;
   } else if(size > xorriso->file_size_limit && xorriso->file_size_limit > 0) {
     sprintf(xorriso->info_text,
             "File exceeds size limit of %.f bytes: %s\n",
             (double) xorriso->file_size_limit,
             Text_shellsafe(disk_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   } else {
     ret= iso_tree_add_new_node(volume, dir, img_name, disk_path, node);
     if(ret<0)
       goto ex;
   }
 }
 if(flag & (256 | 512)) {
   ret= Xorriso_set_hidden(xorriso, (void *) *node, "",  (flag >> 8) & 3, 0);
   if(ret <= 0)
     goto ex;
 }
 if(stbuf_valid && ((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2))) {
   ret= Xorriso_record_dev_inode(xorriso, disk_path,
                            stbuf.st_dev, stbuf.st_ino, (void *) *node, "", 1);
   if(ret <= 0)
     goto ex;
 }

ex:;
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, nominal_source, ret,
                            "Cannot add node to tree", 0, "FAILURE", 1|2);
   return(ret);
 }
 if(LIBISO_ISREG(*node))
   xorriso->pacifier_byte_count+= iso_file_get_size((IsoFile *) *node);
 return(1);
}


/* @param flag bit0= recursion is active
               bit1= do not report added files
               bit6= do not delete eventually existing node from di_array
               bit7= no special handling of split file directories
               bit8= hide in iso_rr
               bit9= hide in joliet
*/
int Xorriso_add_tree(struct XorrisO *xorriso, IsoDir *dir,
                     char *img_dir_path, char *disk_dir_path,
                     struct LinkiteM *link_stack, int flag)
{
 IsoImage *volume;
 IsoNode *node;
 int ret, target_is_dir, source_is_dir, source_is_link, fret, was_failure= 0;
 int do_not_dive, target_is_split= 0, hide_attrs;
 struct DirseQ *dirseq= NULL;
 char *name, *img_name, *srcpt, *stbuf_src= "";
 struct stat stbuf, hstbuf;
 dev_t dir_dev;
 struct LinkiteM *own_link_stack;
 char *sfe= NULL, *sfe2= NULL;
 char *disk_path= NULL, *img_path= NULL, *link_target= NULL;

 /* Avoiding large local memory objects in order to save stack space */
 sfe= malloc(5*SfileadrL);
 sfe2= malloc(5*SfileadrL);
 disk_path= malloc(2*SfileadrL);
 img_path= malloc(2*SfileadrL);
 link_target= calloc(SfileadrL, 1);
 if(sfe==NULL || sfe2==NULL || disk_path==NULL || img_path==NULL ||
    link_target==NULL) {
   Xorriso_no_malloc_memory(xorriso, &sfe, 0);
   {ret= -1; goto ex;}
 }

 own_link_stack= link_stack;

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   goto ex;

 stbuf_src= disk_dir_path;
 if(lstat(disk_dir_path, &stbuf)==-1)
   goto cannot_open_dir;
 dir_dev= stbuf.st_dev;
 if(S_ISLNK(stbuf.st_mode)) {
   if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1))))
     {ret= 2; goto ex;}
   stbuf_src= disk_dir_path;
   if(stat(disk_dir_path, &stbuf)==-1)
     goto cannot_open_dir;
   if(dir_dev != stbuf.st_dev &&
      !(xorriso->do_follow_mount || (xorriso->do_follow_param && !(flag&1))))
     {ret= 2; goto ex;}
 }
 ret= Dirseq_new(&dirseq, disk_dir_path, 1);
 if(ret<0) {
   sprintf(xorriso->info_text,"Failed to create source filesystem iterator");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   {ret= -1; goto ex;}
 }
 if(ret==0) {
cannot_open_dir:;
   Xorriso_msgs_submit(xorriso, 0, disk_dir_path, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text,"Cannot open as source directory: %s",
           Text_shellsafe(disk_dir_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }

 if(Sfile_str(disk_path, disk_dir_path,0)<=0)
   {ret= -1; goto ex;}
 if(disk_path[0]==0 || disk_path[strlen(disk_path)-1]!='/')
   strcat(disk_path,"/");
 name= disk_path+strlen(disk_path);
 if(Sfile_str(img_path, img_dir_path, 0)<=0)
   {ret= -1; goto ex;}
 if(img_path[0] == 0)
   strcat(img_path, "/");
 else if(img_path[strlen(img_path) - 1] != '/')
   strcat(img_path, "/");
 img_name= img_path+strlen(img_path);

 while(1) { /* loop over directory content */
   stbuf_src= "";
   Linkitem_reset_stack(&own_link_stack, link_stack, 0);
   srcpt= disk_path;
   Xorriso_process_msg_queues(xorriso,0);
   ret= Dirseq_next_adr(dirseq,name,0); /* name is a pointer into disk_path */
   if(ret==0)
 break;
   if(ret<0) {
     sprintf(xorriso->info_text,"Failed to obtain next directory entry");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
     {ret= -1; goto ex;}
   }

   /* Compare exclusions against disk_path resp. name */
   ret= Xorriso_path_is_excluded(xorriso, disk_path, 0); /* (is never param) */
   if(ret<0)
     {ret= -1; goto ex;}
   if(ret>0)
 continue;
   /* Check for mkisofs-style hidings */
   hide_attrs= (flag >> 8) & 3;
   if(hide_attrs != 3) {
     ret= Xorriso_path_is_hidden(xorriso, disk_path, 0);
     if(ret<0)
       return(ret);
     if(ret>=0)
       hide_attrs|= ret;
   }

   strcpy(img_name, name);
   if(Xorriso_much_too_long(xorriso, strlen(img_path), 0)<=0)
     {ret= 0; goto was_problem;}
   if(Xorriso_much_too_long(xorriso, strlen(srcpt), 0)<=0)
     {ret= 0; goto was_problem;}
   stbuf_src= srcpt;
   if(lstat(srcpt, &stbuf)==-1) {
cannot_lstat:;
     Xorriso_msgs_submit(xorriso, 0, srcpt, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
             "Cannot determine attributes of source file %s",
             Text_shellsafe(srcpt, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
     ret= 0; goto was_problem;
   }
   source_is_dir= 0;
   source_is_link= S_ISLNK(stbuf.st_mode);
   if(xorriso->do_follow_links && source_is_link) {
     /* Xorriso_hop_link checks for wide link loops */
     ret= Xorriso_hop_link(xorriso, srcpt, &own_link_stack, &hstbuf, 0);
     if(ret<0)
       goto was_problem;
     if(ret==1) {
       ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 0);
       if(ret<=0)
         goto was_problem;
       srcpt= link_target;
       stbuf_src= srcpt;
       if(lstat(srcpt, &stbuf)==-1)
         goto cannot_lstat;
     } else {
       if(Xorriso_eval_problem_status(xorriso, 0, 1|2)<0)
         {ret= 0; goto was_problem;} 
       ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 1);
       if(ret<=0)
         goto was_problem;
     }
   } else if (S_ISLNK(stbuf.st_mode)) {
     ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 1);
     if(ret<=0)
       goto was_problem;
   }
   do_not_dive= 0;
   if(S_ISDIR(stbuf.st_mode)) {
     source_is_dir= 1;
     if(dir_dev != stbuf.st_dev && !xorriso->do_follow_mount)
       do_not_dive= 1;
   }

   /* does a node exist with this name ? */
   node= NULL;
   ret= Xorriso_node_from_path(xorriso, volume, img_path, &node, 1);
   if(ret>0) {
     target_is_dir= LIBISO_ISDIR(node);
     target_is_split= 0;
     if(target_is_dir && !(flag & 128))
       target_is_split= Xorriso_is_split(xorriso, "", (void *) node, 1 | 2);

     if(!((target_is_dir && !target_is_split) && source_is_dir)) {
       Xorriso_process_msg_queues(xorriso,0);

       /* handle overwrite situation */;
       if(xorriso->do_overwrite==1 ||
          (xorriso->do_overwrite==2 && !(target_is_dir && !target_is_split))) {
         ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, img_path,
                          1 | 8 | (flag & 64));
         if(ret<=0)
           goto was_problem;
         if(ret==3) {
           sprintf(xorriso->info_text, "User revoked adding of: %s",
                   Text_shellsafe(img_path, sfe, 0));
           Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
           ret= 0; goto was_problem;
         }
         node= NULL;
       } else {
         Xorriso_msgs_submit(xorriso, 0, srcpt, 0, "ERRFILE", 0);
         sprintf(xorriso->info_text,
     "While grafting %s : file object exists and may not be overwritten by %s",
             Text_shellsafe(img_path,sfe,0), Text_shellsafe(stbuf_src,sfe2,0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         ret= 0; goto was_problem;
       }
     }
   }

   if(node==NULL) {
     ret= Xorriso_tree_graft_node(xorriso, volume, dir, srcpt, img_name,
                                  "", img_path, (off_t) 0, (off_t) 0,
                                  &node, (hide_attrs << 8));
   }
   if(node==NULL) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_msgs_submit(xorriso, 0, stbuf_src, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text, "Grafting failed:  %s = %s",
             Text_shellsafe(img_path,sfe,0), Text_shellsafe(stbuf_src,sfe2,0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto was_problem;
   }

   xorriso->pacifier_count++;
   if((xorriso->pacifier_count%100)==0)
     Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count,
                               xorriso->pacifier_total, "", 0);

   Xorriso_set_change_pending(xorriso, 0);
   if(source_is_dir) {
     if(do_not_dive) {
       sprintf(xorriso->info_text, "Did not follow mount point : %s",
               Text_shellsafe(disk_path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     } else {
       ret= Xorriso_add_tree(xorriso, (IsoDir *) node,
                             img_path, disk_path, own_link_stack,
                             1 | (flag & (2 | 64 | 128)));
     }
     if(ret<=0)
       goto was_problem;
   }

 continue; /* regular bottom of loop */
was_problem:;
   was_failure= 1;
   fret= Xorriso_eval_problem_status(xorriso, ret, 1|2);
   if(fret<0)
     goto ex;
 }

 ret= 1;
ex:
 if(sfe!=NULL)
   free(sfe);
 if(sfe2!=NULL)
   free(sfe2);
 if(disk_path!=NULL)
   free(disk_path);
 if(img_path!=NULL)
   free(img_path);
 if(link_target!=NULL)
   free(link_target);
 Xorriso_process_msg_queues(xorriso,0);
 Linkitem_reset_stack(&own_link_stack, link_stack, 0);
 Dirseq_destroy(&dirseq, 0);
 if(ret<=0)
   return(ret);
 return(!was_failure);
}


/* @param flag bit0= cut_out mode : base on leaf parent directory
               bit1= do not check and perform hidings
*/
int Xorriso_copy_implicit_properties(struct XorrisO *xorriso, IsoDir *dir,
           char *full_img_path, char *img_path, char *full_disk_path, int flag)
{
 int ret, nfic, nic, nfdc, d, i, hide_attrs;
 char nfi[SfileadrL], ni[SfileadrL], nfd[SfileadrL], *cpt;
 char sfe[5*SfileadrL];
 struct stat stbuf;

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, full_img_path, nfi,
                                 1|2);
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, img_path, ni, 1|2);
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, full_disk_path, nfd,
                                 1|2|4);
 if(ret<=0)
   return(ret);
 nfic= Sfile_count_components(nfi, 0);
 nic= Sfile_count_components(ni, 0);
 nfdc= Sfile_count_components(nfd, 0);
 d= nfic-(flag&1)-nic;
 if(d<0)
   return(-1);
 if(d>nfdc)
   return(0);
 for(i= 0; i<d; i++) {
   cpt= strrchr(nfd, '/');
   if(cpt==NULL)
     return(-1); /* should not happen */
   *cpt= 0;
 }
 if(nfd[0]==0)
   strcpy(nfd, "/");
 if(stat(nfd, &stbuf)==-1)
   return(0);
 Xorriso_transfer_properties(xorriso, &stbuf, nfd, (IsoNode *) dir,
                             ((flag&1) && d==0) | 4 | 32);
 sprintf(xorriso->info_text,
         "Copied properties for %s", Text_shellsafe(ni, sfe, 0));
 sprintf(xorriso->info_text+strlen(xorriso->info_text),
         " from %s", Text_shellsafe(nfd, sfe, 0));
 if(!((flag&1) && d==0))
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);

 if(!(flag & 2)) {
   /* Check for mkisofs-style hidings */
   hide_attrs= 0;
   ret= Xorriso_path_is_hidden(xorriso, nfd, 0);
   if(ret<0)
     return(ret);
   if(ret>=0) {
     /* Hide dir */
     ret= Xorriso_set_hidden(xorriso, (void *) dir, "", ret, 0);
     if(ret <= 0)
       return(ret);
   }
 }
 return(1);
}


/* @param bit0= copy link target properties rather than link properties
          bit1= give directory x-permission where is r-permission
          bit2= record dev,inode (only if enabled by xorriso)
*/
int Xorriso_copy_properties(struct XorrisO *xorriso,
                            char *disk_path, char *img_path, int flag)
{
 int ret;
 IsoNode *node;
 struct stat stbuf;

 ret= Xorriso_get_node_by_path(xorriso, img_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 if(flag & 1) {
   if(stat(disk_path, &stbuf)==-1)
     return(0);
 } else {
   if(lstat(disk_path, &stbuf)==-1)
     return(0);
 }
 Xorriso_transfer_properties(xorriso, &stbuf, disk_path, node,
                           ((flag & 2) >> 1) | ((flag & 1) << 5) | (flag & 4));
 Xorriso_set_change_pending(xorriso, 0);
 return(1);
}


/* @param boss_iter Opaque handle to be forwarded to actions in ISO image
                    Set to NULL if calling this function from outside ISO world
   @param flag bit0= mkdir: graft in as empty directory, not as copy from disk
               bit1= do not report added files
               bit2= -follow, -not_*: this is not a command parameter
               bit3= use offset and cut_size for cut_out_node
               bit4= return 3 on rejection by exclusion or user
               bit5= if directory then do not add sub tree
               bit6= do not delete eventually existing node from di_array
               bit7= no special handling of split file directories
               bit8= hide in iso_rr
               bit9= hide in joliet
   @return <=0 = error , 1 = added simple node , 2 = added directory ,
                         3 = rejected 
*/
int Xorriso_graft_in(struct XorrisO *xorriso, void *boss_iter, 
                     char *disk_path, char *img_path,
                     off_t offset, off_t cut_size, int flag)
{
 IsoImage *volume;
 char path[SfileadrL], *apt, *npt, *cpt, sfe[5*SfileadrL], sfe2[5*SfileadrL];
 char *disk_path_pt, resolved_disk_path[SfileadrL];
 IsoDir *dir, *hdir;
 IsoNode *node;
 int done= 0, is_dir= 0, l, ret, target_is_dir, source_is_dir, resolve_link= 0;
 int target_is_split, hide_attrs;
 struct stat stbuf;

 ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&4));
 if(ret<0)
   return(ret);
 if(ret>0)
   return(3*!!(flag&16));

 /* Check for mkisofs-style hidings */
 hide_attrs= (flag >> 8) & 3;
 if(hide_attrs != 3) {
   ret= Xorriso_path_is_hidden(xorriso, disk_path, 0);
   if(ret<0)
     return(ret);
   if(ret>=0)
     hide_attrs|= ret;
 }

 for(cpt= img_path; 1; cpt++) {
   cpt= strstr(cpt,"/.");
   if(cpt==NULL)
 break;
   if(cpt[2]=='.') {
     if(cpt[3]=='/' || cpt[3]==0)
 break;
   } else if(cpt[2]=='/' || cpt[2]==0)
 break;
 }
 if(cpt!=NULL) {
   Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text,
         "Unsupported relative addressing in iso_rr_path %s (disk: %s)",
         Text_shellsafe(img_path, sfe, 0), Text_shellsafe(disk_path, sfe2, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   return(0);
 }

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);

 strncpy(path, img_path, sizeof(path)-1);
 path[sizeof(path)-1]= 0;
 apt= npt= path;

 if(!(flag&1)) {
   ret= lstat(disk_path, &stbuf);
   if(ret!=-1) {
     if(S_ISDIR(stbuf.st_mode))
       is_dir= 1;
     else if((stbuf.st_mode&S_IFMT)==S_IFLNK &&
             (xorriso->do_follow_links ||
              (xorriso->do_follow_param && !(flag&4)))) {
       resolve_link= 1;
       ret= stat(disk_path, &stbuf);
       if(ret!=-1) {
         if(S_ISDIR(stbuf.st_mode))
           is_dir= 1;
       }
     }
   }
   if(ret == -1) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
            "Cannot determine attributes of source file %s",
            Text_shellsafe(disk_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
     return(0);
   }
   if(S_ISDIR(stbuf.st_mode)) {
     is_dir= 1;
   } else {
     l= strlen(img_path);
     if(l>0)
       if(img_path[l-1]=='/')
         l= 0;
     if(l==0) {
       Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
       sprintf(xorriso->info_text,
         "Source %s is not a directory. Target %s would be.",
         Text_shellsafe(disk_path, sfe, 0), Text_shellsafe(img_path, sfe2, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(0);
     }
   }
 }

 dir= iso_image_get_root(volume);
 if(dir==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "While grafting '%s' : no root node available", img_path);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(0);
 }
 for(npt= apt; !done; apt= npt+1) {
   npt= strchr(apt, '/');
   if(npt==NULL) {
     npt= apt+strlen(apt);
     done= 1;
   } else
     *npt= 0;
   if(*apt==0) {
     *apt= '/';
     apt++;
     if(done)
       goto attach_source;
 continue;
   }
   source_is_dir= (is_dir || (flag&1) || !done);
   ret= Xorriso_node_from_path(xorriso, volume, path, &node, 1);
   if(ret>0) {
     target_is_dir= LIBISO_ISDIR(node);

     target_is_split= 0;
     if(target_is_dir && !(flag & 128))
       target_is_split= Xorriso_is_split(xorriso, "", (void *) node, 1 | 2);

     if(!((target_is_dir && !target_is_split) && source_is_dir)) {
       Xorriso_process_msg_queues(xorriso,0);

       /* handle overwrite situation */;
       if(xorriso->do_overwrite==1 ||
          (xorriso->do_overwrite==2 && !(target_is_dir && !target_is_split))) {
         ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, path,
                          1 | 8 | (flag & 64));
         if(ret<=0)
           return(ret);
         if(ret==3) {
           sprintf(xorriso->info_text, "User revoked adding of: %s",
                   Text_shellsafe(disk_path, sfe, 0));
           Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
           return(3*!!(flag&16));
         }
         node= NULL;
         goto handle_path_node;
       }

       Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
       sprintf(xorriso->info_text,
              "While grafting '%s' : '%s' exists and may not be overwritten",
              img_path, path);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(0);
     }
     dir= (IsoDir *) node;
   }

handle_path_node:;
   if(node==NULL && source_is_dir) { /* make a directory */
     ret= iso_tree_add_new_dir(dir, apt, &hdir);
     if(ret<0) {
       Xorriso_process_msg_queues(xorriso,0);
       Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
       Xorriso_report_iso_error(xorriso, img_path, ret,
                                "Cannot create directory", 0, "FAILURE", 1);
       sprintf(xorriso->info_text,
               "While grafting '%s' : could not insert '%s'", img_path, path);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(0);
     }
     dir= hdir;
     Xorriso_set_change_pending(xorriso, 0);
     iso_node_set_ctime((IsoNode *) dir, time(NULL));
     iso_node_set_uid((IsoNode *) dir, geteuid());
     iso_node_set_gid((IsoNode *) dir, getegid());

     if(disk_path!=NULL && !done)
       /* This not only copies disk directory properties
          but also sets eventual hide_attrs */
       Xorriso_copy_implicit_properties(xorriso, dir, img_path, path, disk_path,
                                       !!(flag&8));
   }
   if(done) {
attach_source:;
     if(flag&1) {
       /* directory node was created above */;

     } else if(is_dir) {
       Xorriso_transfer_properties(xorriso, &stbuf, disk_path,
                                   (IsoNode *) dir, 4 | 32);
       if(!(flag&32)) {
         ret= Xorriso_add_tree(xorriso, dir, img_path, disk_path, NULL,
                               flag & (2 | 64 | 128));
         if(ret<=0)
           return(ret);
       }
     } else {
       if(resolve_link) {
         ret= Xorriso_resolve_link(xorriso, disk_path, resolved_disk_path, 0);
         if(ret<=0)
           return(ret);
         disk_path_pt= resolved_disk_path;
       } else
         disk_path_pt= disk_path;

       ret= Xorriso_tree_graft_node(xorriso, volume, dir, disk_path_pt, apt,
                                    disk_path, img_path, offset, cut_size,
                                    &node, (flag&8) | (hide_attrs << 8));
       if(ret<=0) {
         sprintf(xorriso->info_text, "Grafting failed:  %s = %s",
             Text_shellsafe(img_path,sfe,0), Text_shellsafe(disk_path,sfe2,0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         return(0);
       }
       Xorriso_set_change_pending(xorriso, 0);
       iso_node_set_name(node, apt);

       xorriso->pacifier_count++;
       if(xorriso->pacifier_count%100 && !(flag&2))
         Xorriso_pacifier_callback(xorriso, "files added",
                                   xorriso->pacifier_count,
                                   xorriso->pacifier_total, "", 0);
     }
   } else
     *npt= '/';
 }
 Xorriso_process_msg_queues(xorriso,0);
 return(1+!!is_dir);
}


/* @param flag bit0= -follow: disk_path is not a command parameter
*/
int Xorriso_cut_out(struct XorrisO *xorriso, char *disk_path,
                off_t startbyte, off_t bytecount, char *iso_rr_path, int flag)
{
 int ret;
 char eff_source[SfileadrL], eff_dest[SfileadrL], sfe[SfileadrL*5];
 struct stat stbuf;

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_source,
                                 2|4);
 if(ret<=0)
   return(ret);
 ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&1));
 if(ret!=0)
   return(0);

 if(lstat(eff_source, &stbuf)==-1) {
   Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text, "-cut_out: Cannot determine type of %s",
           Text_shellsafe(eff_source, sfe, 0)); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   return(0);
 }

 if((stbuf.st_mode&S_IFMT) == S_IFLNK) {
   if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1))))
     goto unsupported_type;
   if(stat(eff_source, &stbuf)==-1) {
     Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
             "-cut_out: Cannot determine link target type of %s",
             Text_shellsafe(eff_source, sfe, 0)); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
     return(0);
   }
 }
 if(S_ISREG(stbuf.st_mode)) {
   if(stbuf.st_size<startbyte) {
     Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
             "-cut_out: Byte offset %.f larger than file size %.f",
             (double) startbyte, (double) stbuf.st_size); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "SORRY", 0);
     return(0);
   }
 } else {
unsupported_type:;
   Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text, "-cut_out: Unsupported file type (%s) with %s",
           Ftypetxt(stbuf.st_mode, 0), Text_shellsafe(eff_source, sfe, 0)); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   return(0);
 }

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, iso_rr_path, eff_dest,
                                 2);
 if(ret<=0)
   return(ret);

 ret= Xorriso_graft_in(xorriso, NULL, eff_source, eff_dest,
                       startbyte, bytecount, 8);
 return(ret);
}


/* @param flag bit0= do not produce info message on success
   @return 1=success,
           0=was already directory, -1=was other type, -2=other error
*/
int Xorriso_mkdir(struct XorrisO *xorriso, char *path, int flag)
{
 int ret;
 char eff_path[SfileadrL], sfe[5*SfileadrL];

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 1);
 if(ret<0)
   return(-2);
 if(ret>0) {
   sprintf(xorriso->info_text,"-mkdir: Address already existing %s",
          Text_shellsafe(eff_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                       (ret==2 ? "WARNING" : "FAILURE"), 0);
   return(-1+(ret==2));
 }
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 2);
 if(ret<0)
   return(-2);
 ret= Xorriso_graft_in(xorriso, NULL, NULL, eff_path, (off_t) 0, (off_t) 0, 1);
 if(ret<=0)
   return(-2);
 if(!(flag&1)) {
   sprintf(xorriso->info_text, "Created directory in ISO image: %s\n",
           Text_shellsafe(eff_path,sfe,0));
   Xorriso_info(xorriso, 0);
 }
 return(1);
}


/* @param boss_iter  If not NULL then this is an iterator suitable for
               iso_dir_iter_remove()  which is then to be used instead
               of iso_node_remove().
   @param flag bit0= remove whole sub tree: rm -r
               bit1= remove empty directory: rmdir  
               bit2= recursion: do not reassure in mode 2 "tree"
               bit3= this is for overwriting and not for plain removal
               bit4= count deleted files in xorriso->pacifier_count
               bit5= with bit0 only remove directory content, not the directory
               bit6= do not delete eventually existing node from di_array
   @return   <=0 = error
               1 = removed simple node 
               2 = removed directory or tree
               3 = did not remove on user revocation
*/
int Xorriso_rmi(struct XorrisO *xorriso, void *boss_iter, off_t boss_mem,
                char *path, int flag)
{
 int ret, is_dir= 0, pl, not_removed= 0, fret;
 IsoNode *victim_node, *node;
 IsoDir *boss_node, *root_dir;
 IsoDirIter *iter= NULL;
 IsoImage *volume;
 char *sub_name, *name;
 char *sfe= NULL, *sub_path= NULL;
 off_t mem;
 IsoNode **node_array= NULL;
 int node_count= 0, node_idx;

 /* Avoiding large local memory objects in order to save stack space */
 sfe= malloc(5*SfileadrL);
 sub_path= malloc(2*SfileadrL);
 if(sfe==NULL || sub_path==NULL) {
   Xorriso_no_malloc_memory(xorriso, &sfe, 0);
   {ret= -1; goto ex;}
 }

#ifndef Libisofs_iso_dir_iter_sufficienT
 /* Ticket 127: A80301 - A80302
    I do not not deem IsoDirIter safe for node list manipulations.
    The parameter boss_iter once was intended to allow such but
    has now been downgraded to a mere check for eventual programming bugs.
 */
 if(boss_iter!=NULL) {
   sprintf(xorriso->info_text,
       "Program error: Xorriso_rmi() was requested to delete iterated node %s",
       Text_shellsafe(path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   ret= -1; goto ex;
 }
#endif /* Libisofs_iso_dir_iter_sufficienT */

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   goto ex;
 
 if(Xorriso_much_too_long(xorriso, strlen(path), 0)<=0)
   {ret= 0; goto ex;}
 ret= Xorriso_node_from_path(xorriso, volume, path, &victim_node, 0);
 if(ret<=0)
   goto ex;
 root_dir= iso_image_get_root(volume);
 if(((void *) root_dir) == ((void *) victim_node)) {
   sprintf(xorriso->info_text, "May not delete root directory");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }

 if(LIBISO_ISDIR(victim_node))
   is_dir= 1;
 if(!is_dir) {
   if(flag&2) { /* rmdir */
     sprintf(xorriso->info_text, "%s in loaded ISO image is not a directory",
             Text_shellsafe(path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
 } else {
   if(flag&1) { /* rm -r */
     if((xorriso->do_reassure==1 && !xorriso->request_not_to_ask) ||
        (flag&32)) {
       /* Iterate over subordinates and delete them */
       mem= boss_mem;

       ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem,
                         &iter, &node_array, &node_count, &node_idx,
                         &node, 1|2);
       if(ret<=0) {
cannot_create_iter:;
         Xorriso_cannot_create_iter(xorriso, ret, 0);
         ret= -1; goto ex;
       }
       pl= strlen(path);
       strcpy(sub_path, path);
       if(pl==0 || sub_path[pl-1]!='/') {
         sub_path[pl++]= '/';
         sub_path[pl]= 0;
       }
       sub_name= sub_path+pl;
       while(1) { 
         ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter,
                                &node_array, &node_count, &node_idx, &node, 0);
         if(ret<0)
           goto ex;
         if(ret==0 || xorriso->request_to_abort)
       break;
         name= (char *) iso_node_get_name(node);
         if(Xorriso_much_too_long(xorriso, pl+1+strlen(name), 0)<=0)
           {ret= 0; goto rm_r_problem_handler;}
         strcpy(sub_name, name);
         ret= Xorriso_rmi(xorriso, iter, mem, sub_path,
                          (flag & ( 1 | 2 | 8 | 16 | 64)) | 4);
         if(ret==3 || ret<=0 || xorriso->request_to_abort) {
rm_r_problem_handler:;
           not_removed= 1;
           fret= Xorriso_eval_problem_status(xorriso, ret, 1|2);
           if(fret<0)
             goto dir_not_removed;
         }
       }
       if(flag&32)
         {ret= 2; goto ex;}

       if(not_removed) {
dir_not_removed:;
         sprintf(xorriso->info_text, "Directory not removed: %s",
                 Text_shellsafe(path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
         if(ret>0)
           ret= 3;
         goto ex;
       }
     }
   } else {
     if(!(flag&2)) { /* not rmdir */
       sprintf(xorriso->info_text, "%s in loaded ISO image is a directory",
               Text_shellsafe(path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       ret= 0; goto ex;
     }

     ret= iso_dir_get_children((IsoDir *) victim_node, &iter);
     Xorriso_process_msg_queues(xorriso,0);
     if(ret<0)
       goto cannot_create_iter;
     if(ret>0) {
       if(iso_dir_iter_next(iter, &node) == 1) {
         sprintf(xorriso->info_text,
                 "Directory not empty on attempt to delete: %s",
                 Text_shellsafe(path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         ret= 0; goto ex;
       }
     }
   }
 }

 if(xorriso->request_to_abort)
   {ret= 3; goto ex;}
 boss_node= iso_node_get_parent(victim_node);
 Xorriso_process_msg_queues(xorriso,0);
 if(boss_node==NULL) {
   sprintf(xorriso->info_text,
           "Cannot find parent node of %s in loaded ISO image",
           Text_shellsafe(path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 while((xorriso->do_reassure==1 || (xorriso->do_reassure==2 && !(flag&4)))
       && !xorriso->request_not_to_ask) {
   /* ls -ld */
   Xorriso_ls_filev(xorriso, xorriso->wdi, 1, &path, (off_t) 0, 1|2|8);
   if(is_dir) /* du -s */
     Xorriso_ls_filev(xorriso, xorriso->wdi, 1, &path, (off_t) 0, 2|4);
   if(flag&8)
     sprintf(xorriso->info_text,
  "File exists. Remove ?  n= keep old, y= remove, x= abort, @= stop asking\n");
   else
     sprintf(xorriso->info_text,
  "Remove above file ?  n= keep it, y= remove it, x= abort, @= stop asking\n");
   Xorriso_info(xorriso, 4);
   ret= Xorriso_request_confirmation(xorriso, 1|2|4|16);
   if(ret<=0)
     goto ex;
   if(xorriso->request_to_abort) {
     sprintf(xorriso->info_text,
             "Removal operation aborted by user before file: %s",
             Text_shellsafe(path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     ret= 3; goto ex;
   }
   if(ret==3)
 continue;
   if(ret==6) /* yes */
 break;
   if(ret==4) { /* yes, do not ask again */
     xorriso->request_not_to_ask= 1;
 break;
   }
   if(ret==1) { /* no */
     sprintf(xorriso->info_text, "Kept in existing state: %s",
             Text_shellsafe(path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     ret= 3; goto ex;
   }
 }

 if(!(flag & 64))
   Xorriso_invalidate_di_item(xorriso, victim_node, 0);

#ifdef Libisofs_iso_dir_iter_sufficienT

 if(boss_iter!=NULL) {
   ret= iso_dir_iter_remove((IsoDirIter *) boss_iter);
   if(ret<0)
     ret= -1;
 } else
   ret= iso_node_remove(victim_node);

#else /* ! Libisofs_iso_dir_iter_sufficienT */

 ret= iso_node_remove(victim_node);

#endif /* Libisofs_iso_dir_iter_sufficienT */

 Xorriso_process_msg_queues(xorriso,0);
 if(ret<0) {
   Xorriso_report_iso_error(xorriso, path, ret, "Cannot remove node", 0,
                            "FATAL", 1);
   sprintf(xorriso->info_text,
           "Internal failure to remove %s from loaded ISO image",
           Text_shellsafe(path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   ret= -1; goto ex;
 }

 if(flag&16)
   xorriso->pacifier_count++;
 Xorriso_set_change_pending(xorriso, 0);
 ret= 1+!!is_dir;
ex:;
 if(sfe!=NULL)
   free(sfe);
 if(sub_path!=NULL)
   free(sub_path);
 Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter,
                    &node_array, &node_count, &node_idx, &node, (1<<31));
 return(ret);
} 


/* @param boss_iter Opaque handle to be forwarded to actions in ISO image
                    Set to NULL if calling this function from outside ISO world
*/
int Xorriso_rename(struct XorrisO *xorriso, void *boss_iter,
                   char *origin, char *dest, int flag)
{
 int ret, ol, dest_ret;
 char sfe[5*SfileadrL], eff_dest[SfileadrL], dir_adr[SfileadrL], *cpt;
 char *leafname, eff_origin[SfileadrL], sfe2[5*SfileadrL], *old_leafname;
 IsoImage *volume;
 IsoDir *origin_dir, *dest_dir;
 IsoNode *node, *iso_node;

#ifndef Libisofs_iso_dir_iter_sufficienT
 /* Ticket 127: A80301 - A80302
    I do not not deem IsoDirIter safe for node list manipulations.
    The parameter boss_iter once was intended to allow such but
    has now been downgraded to a mere check for eventual programming bugs.
 */
 if(boss_iter!=NULL) {
   sprintf(xorriso->info_text,
    "Program error: Xorriso_rename() was requested to delete iterated node %s",
       Text_shellsafe(origin, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
#endif /* Libisofs_iso_dir_iter_sufficienT */

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, origin, eff_origin, 0);
 if(ret<=0)
   return(ret);
 dest_ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest,1);
 if(dest_ret<0)
   return(dest_ret);
 if(dest_ret==0) { /* obtain eff_dest address despite it does not exist */
   ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest, 2);
   if(ret<=0)
     return(ret);
 }

 /* Prevent that destination is a subordinate of origin
    (that would be a black hole plopping out of the universe) */
 ol= strlen(eff_origin);
 if(ol==0) {
   sprintf(xorriso->info_text, "May not rename root directory");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 } else if(strcmp(eff_origin, eff_dest)==0) {
   sprintf(xorriso->info_text, "Ignored attempt to rename %s to itself",
           Text_shellsafe(eff_origin,sfe,0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
   return(0);
 } else if(strncmp(eff_origin, eff_dest, ol)==0 &&
           (eff_dest[ol]==0 || eff_dest[ol]=='/')) {
   sprintf(xorriso->info_text,
           "May not rename %s to its own sub address %s",
           Text_shellsafe(eff_origin,sfe,0), Text_shellsafe(eff_dest,sfe2,0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }

 /* Check whether destination exists and may be not overwriteable */
 if(dest_ret==2 && xorriso->do_overwrite!=1) {
   sprintf(xorriso->info_text, "Renaming may not overwrite directory: %s",
           Text_shellsafe(eff_dest, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 } else if (dest_ret==1 && !xorriso->do_overwrite) {
   sprintf(xorriso->info_text, "Renaming may not overwite: %s",
           Text_shellsafe(eff_dest, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 } else if(dest_ret>0) {
   ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, eff_dest, 1|8);
   if(ret<=0)
     return(0);
   if(ret==3) {
     sprintf(xorriso->info_text, "User revoked renaming of: %s",
             Text_shellsafe(eff_origin, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     return(0);
   }
 }

 /* Ensure existence of destination directory */
 strcpy(dir_adr, eff_dest);
 cpt= strrchr(dir_adr, '/');
 if(cpt==NULL)
   cpt= dir_adr+strlen(dir_adr);
 *cpt= 0;
 if(dir_adr[0]!=0) {
   ret= Xorriso_graft_in(xorriso, boss_iter, NULL, dir_adr,
                         (off_t) 0, (off_t) 0, 1);
   if(ret<=0)
     return(ret);
 }

 /* Move node */
 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);
 Xorriso_node_from_path(xorriso, volume, dir_adr, &iso_node, 0);
 dest_dir= (IsoDir *) iso_node;
 strcpy(dir_adr, eff_origin);
 cpt= strrchr(dir_adr, '/');
 if(cpt==NULL)
   cpt= dir_adr+strlen(dir_adr);
 *cpt= 0;
 Xorriso_node_from_path(xorriso, volume, dir_adr, &iso_node, 0);
 origin_dir= (IsoDir *) iso_node;
 Xorriso_node_from_path(xorriso, volume, eff_origin, &node, 0);
 if(dest_dir==NULL || origin_dir==NULL || node==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "Internal error on rename: confirmed node turns out as NULL");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 ret= iso_node_take(node);
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot take", 0, "FATAL",1);
   sprintf(xorriso->info_text,
           "Internal error on rename: failed to take node");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 leafname= strrchr(eff_dest, '/');
 if(leafname==NULL)
   leafname= eff_dest;
 else
   leafname++;
 
 old_leafname= (char *) iso_node_get_name(node);
 if(strcmp(leafname, old_leafname)!=0)
   ret= iso_node_set_name(node, leafname);
 else
   ret= 1;
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot set name", 0,
                            "FAILURE", 1);
   return(-1);
 }
 Xorriso_process_msg_queues(xorriso,0);
 ret= iso_dir_add_node(dest_dir, node, 0);
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot add", 0, "FATAL", 1);
   sprintf(xorriso->info_text,
           "Internal error on rename: failed to insert node");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 return(1);
}


int Xorriso_set_st_mode(struct XorrisO *xorriso, char *in_path,
                        mode_t mode_and, mode_t mode_or, int flag)
{
 mode_t mode= 0;
 int ret;
 IsoNode *node;
 char sfe[5*SfileadrL], path[SfileadrL];

 ret= Xorriso_get_node_by_path(xorriso, in_path, path, &node, 0);
 if(ret<=0)
   return(ret);
 mode= iso_node_get_permissions(node);
 mode= (mode & mode_and) | mode_or;
 iso_node_set_permissions(node, mode);
 iso_node_set_ctime(node, time(NULL));
 sprintf(xorriso->info_text,"Permissions now: %-5.5o  %s",
         (unsigned int) (mode & 0xffff), Text_shellsafe(path, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 Xorriso_set_change_pending(xorriso, 0);
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso_set_uid(struct XorrisO *xorriso, char *in_path, uid_t uid,
                    int flag)
{
 int ret;
 IsoNode *node;

 ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 iso_node_set_uid(node, uid);
 iso_node_set_ctime(node, time(NULL));
 Xorriso_set_change_pending(xorriso, 0);
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso_set_gid(struct XorrisO *xorriso, char *in_path, gid_t gid,
                    int flag)
{
 int ret;
 IsoNode *node;

 ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 iso_node_set_gid(node, gid);
 iso_node_set_ctime(node, time(NULL));
 Xorriso_set_change_pending(xorriso, 0);
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


/* @parm flag  bit0= atime, bit1= ctime, bit2= mtime, bit8=no auto ctime */
int Xorriso_set_time(struct XorrisO *xorriso, char *in_path, time_t t,
                    int flag)
{
 int ret;
 IsoNode *node;

 ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 if(flag&1)
   iso_node_set_atime(node, t);
 if(flag&2)
   iso_node_set_ctime(node, t);
 if(flag&4)
   iso_node_set_mtime(node, t);
 if(!(flag&(2|256)))
   iso_node_set_ctime(node, time(NULL));
 Xorriso_set_change_pending(xorriso, 0);
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


/*
  Apply the effect of mkisofs -r to a single node
*/
int Xorriso_mkisofs_lower_r(struct XorrisO *xorriso, IsoNode *node, int flag)
{
 mode_t perms;

 perms= iso_node_get_permissions(node);
 iso_node_set_uid(node, (uid_t) 0);
 iso_node_set_gid(node, (gid_t) 0);
 perms|= S_IRUSR | S_IRGRP | S_IROTH;
 perms&= ~(S_IWUSR | S_IWGRP | S_IWOTH);
 if(perms & (S_IXUSR | S_IXGRP | S_IXOTH))
   perms|= (S_IXUSR | S_IXGRP | S_IXOTH);
 perms&= ~(S_ISUID | S_ISGID | S_ISVTX);
 iso_node_set_permissions(node, perms);
 return(1);
}


/* @param node      Opaque handle to IsoNode which is to be manipulated
                    instead of path if it is not NULL.
   @param path      is used as address if node is NULL.
   @param access_text  "access" ACL in long text form
   @param default_text "default" ACL in long text form
   @param flag      bit0= do not warn of root directory if not capable of AAIP
   @return          >0 success , <=0 failure
*/
int Xorriso_setfacl(struct XorrisO *xorriso, void *in_node, char *path,
                    char *access_text, char *default_text, int flag)
{
 int ret;
 IsoNode *node;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     goto ex;
 }
 ret= iso_node_set_acl_text(node, access_text, default_text, 0);
 if(ret <= 0) {
   if(path != NULL && path[0] != 0) {
     strcpy(xorriso->info_text, "Error with setting ACL of ");
     Text_shellsafe(path, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   ret= 0; goto ex;
 }
 Xorriso_set_change_pending(xorriso, 0);
 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/* @param in_node       Opaque handle to IsoNode which is to be manipulated
                        instead of path if it is not NULL.
   @param path          is used as address if node is NULL.
   @param num_attrs     Number of attributes
   @param names         Array of pointers to 0 terminated name strings
   @param value_lengths Array of byte lengths for each attribute payload
   @param values        Array of pointers to the attribute payload bytes
   @param flag          bit0= Do not maintain eventual existing ACL of the node
                        bit1= Do not clear the existing attribute list
                        bit2= Delete the attributes with the given names
                        bit3= Allow non-user attributes.
                        bit4= do not warn of root if incapable of AAIP
   @return              >0 success , <=0 failure
*/
int Xorriso_setfattr(struct XorrisO *xorriso, void *in_node, char *path,
                     size_t num_attrs, char **names,
                     size_t *value_lengths, char **values, int flag)
{
 int ret;
 IsoNode *node;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     goto ex;
 }
 ret= iso_node_set_attrs(node, num_attrs, names, value_lengths, values,
                         flag & (1 | 2 | 4 | 8));
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0) {
   Xorriso_report_iso_error(xorriso, "", ret,
                            "Error when setting ACL and xattr to image node",
                            0, "FAILURE", 1);
   if(path != NULL && path[0] != 0) {
     strcpy(xorriso->info_text, "Error with setting xattr of ");
     Text_shellsafe(path, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   ret= 0; goto ex;
 }
 Xorriso_set_change_pending(xorriso, 0);
 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/*
   @param flag bit0= use parameters dev,ino rather than disk_path
               bit1= compare attribute rather than setting it
                     return: 0=dev,ino match, 1=mismatch, 2=no node attribute
                            -1=error
               bit5= if not bit0:
                     transfer dev,inode from eventual link target
               bit7= omit dev check mit bit1
*/
int Xorriso_record_dev_inode(struct XorrisO *xorriso, char *disk_path,
                             dev_t dev, ino_t ino,
                             void *in_node, char *iso_path, int flag)
{
 size_t l, di_l= 0;
 int i, ret;
 dev_t hdev;
 ino_t hino;
 char buf[66], *bufpt, *wpt, *di= NULL;
 static char *name= "isofs.di";
 struct stat stbuf;

 if(!(flag & 1)) {
   if(flag & 32) {
     if(stat(disk_path, &stbuf) == -1)
       return(-1);
   } else {
     if(lstat(disk_path, &stbuf) == -1)
       return(-1);
   }
   dev= stbuf.st_dev;
   ino= stbuf.st_ino;
 }
   
 wpt= buf;
 hdev= dev;
 for(i= 0; hdev != 0; i++)
   hdev= hdev >> 8;
 l= i;
 *(wpt++)= l;
 for(i= 0; i < l; i++)
   *(wpt++)= dev >> (8 * (l - i - 1));
 hino= ino;
 for(i= 0; hino != 0; i++)
   hino= hino >> 8;
 l= i;
 *(wpt++)= l;
 for(i= 0; i < l; i++)
   *(wpt++)= ino >> (8 * (l - i - 1));
 l= wpt - buf; 
 bufpt= buf;

 if(flag & 2) {
   /* Compare node attribute with bufpt,l */
   ret= Xorriso_get_attr_value(xorriso, in_node, iso_path,
                               "isofs.di", &di_l, &di, 0);
   if(ret < 0)
     goto ex;
   if(ret == 0)
     {ret= 2; goto ex;}
   if(flag & 128) {
     if(di_l <= 0)
       {ret= 1; goto ex;}
     hino= 0;
     for(i= di[0] + 2; i < di_l && i - di[0] - 2 < di[(int) di[0] + 1]; i++)
       hino= (hino << 8) | ((unsigned char *) di)[i];
     if(hino != ino)
       {ret= 1; goto ex;} 
   } else {
     if(l != di_l)
       {ret= 1; goto ex;}
     for(i= 0; i < l; i++)
       if(di[i] != buf[i])
         {ret= 1; goto ex;} 
   }
   ret= 0;
 } else {
   ret= Xorriso_setfattr(xorriso, in_node, iso_path,
                         (size_t) 1, &name, &l, &bufpt, 2 | 8);
 }
ex:;
 if(di != NULL)
   free(di);
 return(ret);
}


/* @return  see Xorriso_update_interpreter()
*/
int Xorriso_widen_hardlink(struct XorrisO *xorriso, void * boss_iter,
                           IsoNode *node,
                           char *abs_path, char *iso_prefix, char *disk_prefix,
                           int flag)
{
 int ret= 0, idx, low, high, i, do_widen= 0, compare_result= 0;
 char disk_path[SfileadrL];

 /* Lookup all di_array instances of node */
 if(LIBISO_ISDIR(node))
   return(3);
 ret= Xorriso_search_di_range(xorriso, node, &idx, &low, &high, 2);
 if(ret <= 0)
   return(3);
 /* Check and reset di_do_widen bits */
 for(i= low; i <= high; i++) {
   if(node != xorriso->di_array[i]) /* might be NULL */
 continue;
   if(xorriso->di_do_widen[i / 8] & (1 << (i % 8)))
     do_widen= 1;
   xorriso->di_do_widen[i / 8]&= ~(1 << (i % 8));
 }
 if(idx < 0 || !do_widen)
   return(3);

 ret= Xorriso_pfx_disk_path(xorriso, abs_path, iso_prefix, disk_prefix,
                            disk_path, 0);
 if(ret <= 0)
   return(ret);
 ret= Sfile_type(disk_path, 1);
 if(ret < 0)
   return(3); /* does not exist on disk */

 /* >>> compare_result bit17 = is_split */;

 ret= Xorriso_update_interpreter(xorriso, boss_iter, compare_result, disk_path,
                                 abs_path, 1);
 if(ret <= 0)
   return(ret);
 return(ret);
}


int Xorriso_set_hidden(struct XorrisO *xorriso, void *in_node, char *path,
                       int hide_state, int flag)
{
 int ret, hide_attrs= 0;
 IsoNode *node;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     return(ret);
 }
 if(hide_state) {
   hide_attrs|= LIBISO_HIDE_BUT_WRITE;
   if(hide_state & 1)
     hide_attrs|= LIBISO_HIDE_ON_RR;
   if(hide_state & 2)
     hide_attrs|= LIBISO_HIDE_ON_JOLIET;
 }
 iso_node_set_hidden(node, hide_attrs);
 return(1);
}


int Xorriso_cannot_create_iter(struct XorrisO *xorriso, int iso_error,int flag)
{
 Xorriso_process_msg_queues(xorriso,0);
 Xorriso_report_iso_error(xorriso, "", iso_error, "Cannot create iter", 0,
                          "FATAL", 1);
 sprintf(xorriso->info_text, "Cannot create IsoDirIter object");
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
 return(1);
}


/* The caller shall make no assumptions about the meaning of iter, node_array,
   node_count, node_idx ! They are just opaque handles for which the caller
   provides the memory of proper type.
   @param flag bit0= initialize iteration
               bit1= action needs full freedom of object manipulation
               bit2= action needs LBA sorted iteration
               bit31= end iteration (mandatory !)
*/
int Xorriso_findi_iter(struct XorrisO *xorriso, IsoDir *dir_node, off_t *mem,
                       IsoDirIter **iter, 
                       IsoNode ***node_array, int *node_count, int *node_idx,
                       IsoNode **iterated_node, int flag)
{
 int ret, i;
 IsoNode *node;
 off_t new_mem= 0;
 char mem_text[80], limit_text[80];

 if(flag&1) {
   *node_array= NULL;
   *node_count= -1;
   *node_idx= 0;
   *iter= NULL;
   ret= iso_dir_get_children(dir_node, iter);
   if(ret<0) {
cannot_iter:;
     Xorriso_cannot_create_iter(xorriso, ret, 0);
     return(-1);
   }
   if((flag&2)|(flag&4)) {
     /* copy list of nodes and prepare soft iterator */
     *node_count= 0;
     while(iso_dir_iter_next(*iter, &node) == 1)
       (*node_count)++;
     iso_dir_iter_free(*iter);
     *iter= NULL;

     new_mem= ((*node_count)+1) * sizeof(IsoNode *);
     if(new_mem > xorriso->temp_mem_limit) {
       Sfile_scale((double) new_mem, mem_text, 5,1e4, 0);
       Sfile_scale((double) xorriso->temp_mem_limit, limit_text, 5,1e4, 0);
       sprintf(xorriso->info_text,
           "Stacked directory snapshots exceed -temp_mem_limit (%s > %s)",
           mem_text, limit_text);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       *node_count= -1;
       return(-1);
     }
     (*node_array)= (IsoNode **) calloc((*node_count)+1, sizeof(IsoNode *));
     if(*node_array == NULL) {
       sprintf(xorriso->info_text,
               "Could not allocate inode list of %.f bytes",
               ((double) (*node_count)+1) * (double) sizeof(IsoNode *));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
       *node_count= -1;
       return(-1);
     }
     *mem= new_mem;
     ret= iso_dir_get_children(dir_node, iter);
     if(ret<0)
       goto cannot_iter;
     while(iso_dir_iter_next(*iter, &node) == 1 && *node_idx < *node_count) {
       (*node_array)[*node_idx]= node;
       iso_node_ref(node);
       (*node_idx)++;
     }
     iso_dir_iter_free(*iter);
     *iter= NULL;
     *node_count= *node_idx;
     *node_idx= 0;
     if((flag&4) && *node_count>1)
       qsort(*node_array, *node_count, sizeof(IsoNode *),
             Xorriso__node_lba_cmp);
   }
 }

 if(flag&(1<<31)) {
   if(*node_count>=0 && *node_array!=NULL) {
     for(i= 0; i<*node_count; i++)
       iso_node_unref((*node_array)[i]);
     free(*node_array);
     *node_array= NULL;
     *node_count= -1;
     *node_idx= 0;
   } else {
     if(*iter!=NULL)
       iso_dir_iter_free(*iter);
     *iter= NULL;
   }
 }

 if(flag&(1|(1<<31))) 
   return(1);
 if(*node_count>=0) {
   /* return next node_array element */
   if(*node_idx>=*node_count)
     return(0);
   *iterated_node= (*node_array)[*node_idx];
   (*node_idx)++;
 } else {
   ret= iso_dir_iter_next(*iter, iterated_node);
   return(ret == 1);
 }
 return(1);
}


/* @param flag bit0= not a command parameter (directory iteration or recursion)
               bit1= do not count deleted files with rm and rm_r
   @return <=0 error,
             1=ok
             2=ok, node has been deleted,
             3=ok, do not dive into directory (e.g. because it is a split file)
*/
int Xorriso_findi_action(struct XorrisO *xorriso, struct FindjoB *job,
                         IsoDirIter *boss_iter, off_t boss_mem,
                         char *abs_path, char *show_path, 
                         IsoNode *node, int depth, int flag)
{
 int ret= 0, type, action= 0, hflag, deleted= 0, no_dive= 0;
 uid_t user= 0;
 gid_t group= 0;
 time_t date= 0;
 mode_t mode_or= 0, mode_and= ~1;
 char *target, *text_2, sfe[5*SfileadrL], *iso_prefix, md5[16];
 struct FindjoB *subjob;
 struct stat dir_stbuf;

 action= Findjob_get_action_parms(job, &target, &text_2, &user, &group,
                                &mode_and, &mode_or, &type, &date, &subjob, 0);
 if(action<0)
   action= 0;

 hflag= 16*!(flag&2);
 ret= 1;
 if(action==1) { /* rm (including rmdir) */
   ret= Xorriso_fake_stbuf(xorriso, abs_path, &dir_stbuf, &node, 1);
   if(ret>0) {
     if(S_ISDIR(dir_stbuf.st_mode))
       hflag= 2;
     ret= Xorriso_rmi(xorriso, boss_iter, boss_mem, abs_path, hflag);
     deleted= 1;
   }
 } else if(action==2) { /* rm_r */
   ret= Xorriso_rmi(xorriso, boss_iter, boss_mem, abs_path, 1|hflag);
   deleted= 1;
 } else if(action==3) {

   /* >>> mv target */;

 } else if(action==4) { /* chown */
   ret= Xorriso_set_uid(xorriso, abs_path, user, 0);
 } else if(action==5) { /* chgrp */
   ret= Xorriso_set_gid(xorriso, abs_path, group, 0);
 } else if(action==6) { /* chmod */
   ret= Xorriso_set_st_mode(xorriso, abs_path, mode_and, mode_or, 0);
 } else if(action==7) { /* alter_date */
   ret= Xorriso_set_time(xorriso, abs_path, date, type&7);
 } else if(action==8) { /* lsdl */
   ret= Xorriso_ls_filev(xorriso, "", 1, &abs_path, (off_t) 0, 1|2|8);
 } else if(action>=9 && action<=13) { /* actions which have own findjobs */
   Findjob_set_start_path(subjob, abs_path, 0);
   ret= Xorriso_findi(xorriso, subjob, boss_iter, boss_mem, NULL,
                      abs_path, &dir_stbuf, depth, 1);
 } else if(action==14 || action==17) { /* compare , update */
   Findjob_get_start_path(job, &iso_prefix, 0);
   ret= Xorriso_find_compare(xorriso, (void *) boss_iter, abs_path,
                             iso_prefix, target, (action==17)|((flag&1)<<1));
   if(ret==2)
     deleted= 1;
   if(ret==3)
     no_dive= 1;
   if(ret>=0)
     ret= 1;
 } else if(action==16 || action==18) { /* not_in_iso , add_missing */
   ;
 } else if(action == 21) { /* report_damage */
   ret= Xorriso_report_damage(xorriso, show_path, node, 0);
 } else if(action == 22) {
   ret= Xorriso_report_lba(xorriso, show_path, node, 0);
 } else if(action == 23) { /* internal: memorize path of last matching node */
   ret= Findjob_set_found_path(job, show_path, 0);
 } else if(action == 24) {
   ret= Xorriso_getfacl(xorriso, (void *) node, show_path, NULL, 0);
 } else if(action == 25) {
   if(target == NULL || target[0] || text_2 == NULL || text_2[0])
     ret= Xorriso_setfacl(xorriso, (void *) node, show_path, target, text_2,0);
 } else if(action == 26) {
   ret= Xorriso_getfattr(xorriso, (void *) node, show_path, NULL, 0);
 } else if(action == 27) {
   ret= Xorriso_path_setfattr(xorriso, (void *) node, show_path,
                              target, strlen(text_2), text_2, 0);
 } else if(action == 28) { /* set_filter */
   ret= Xorriso_set_filter(xorriso, (void *) node, show_path, target, 1 | 2);
 } else if(action == 29) { /* show_stream */
   ret= Xorriso_show_stream(xorriso, (void *) node, show_path, 1 | 2);
 } else if(action == 30) { /* internal: count */
   xorriso->node_counter++;
 } else if(action == 31) { /* internal: register */
   if(xorriso->node_counter < xorriso->node_array_size) {
     xorriso->node_array[xorriso->node_counter++]= (void *) node;
     iso_node_ref(node); /* In case node gets deleted from tree during
                            the lifetime of xorriso->node_array */
   }
 } else if(action == 32) { /* internal: widen_hardlinks disk_equiv */
   Findjob_get_start_path(job, &iso_prefix, 0);
   ret= Xorriso_widen_hardlink(xorriso, (void *) boss_iter, node, abs_path,
                               iso_prefix, target, 0);
   if(ret==2)
     deleted= 1;
 } else if(action == 33) { /* get_any_xattr */
   ret= Xorriso_getfattr(xorriso, (void *) node, show_path, NULL, 8);
 } else if(action == 34) { /* get_md5 */
   ret= Xorriso_get_md5(xorriso, (void *) node, show_path, md5, 0);
   if(ret >= 0)
     ret= 1;
 } else if(action == 35) { /* check_md5 */
   ret= Xorriso_check_md5(xorriso, (void *) node, show_path, 2);
   if(ret == 0)
     xorriso->find_check_md5_result|= 1;
   else if(ret < 0)
     xorriso->find_check_md5_result|= 2;
   else if(ret == 1)
     xorriso->find_check_md5_result|= 8;
   else if(ret == 2)
     xorriso->find_check_md5_result|= 4;
   if(ret >= 0)
     ret= 1;
 } else if(action == 36) { /* make_md5 */
   ret= Xorriso_make_md5(xorriso, (void *) node, show_path, 0);
   if(ret >= 0)
     ret= 1;
 } else if(action == 37) { /* mkisofs_r */
   ret= Xorriso_mkisofs_lower_r(xorriso, node, 0);
 } else if(action == 38) { /* sort_weight */
   iso_node_set_sort_weight(node, type);
 } else if(action == 39) { /* hide */
   Xorriso_set_hidden(xorriso, node, NULL, type, 0);
 } else { /* includes : 15 in_iso */
   sprintf(xorriso->result_line, "%s\n", Text_shellsafe(show_path, sfe, 0));
   Xorriso_result(xorriso, 0);
   ret= 1;
 }
 if(ret<=0)
   return(ret);
 if(deleted)
   return(2);
 if(no_dive)
   return(3);
 return(1);
}


int Exprtest_match_disk_name(struct XorrisO *xorriso, struct ExprtesT *ftest,
                             IsoNode *node, int flag)

{
 int ret;
 char disk_path[SfileadrL], *npt;
 regmatch_t name_match;
 void *arg2;

 ret= Xorriso_retrieve_disk_path(xorriso, node, disk_path, 0);
 if(ret <= 0)
   return(0);
 arg2= ftest->arg2;
 npt= strrchr(disk_path, '/');
 if(npt != NULL)
   npt++;
 else
   npt= disk_path;
 ret= regexec(arg2, npt, 1, &name_match, 0);
 return !ret;
}


int Exprtest_match(struct XorrisO *xorriso, struct ExprtesT *ftest,
                   void *node_pt, char *name, char *path,
                   struct stat *boss_stbuf, struct stat *stbuf, int flag)
/*
return:
 <0 = error
  0 = does not match
  1 = does match
  2 = immediate decision : does not match
  3 = immediate decision : does match
*/
{
 int value=0, ret, start_lba, end_lba;
 int lba_count, *file_end_lbas= NULL, *file_start_lbas= NULL, i, mask;
 void *arg1, *arg2;
 char ft, *decision, md5[16];
 regmatch_t name_match;
 off_t damage_start, damage_end, size;
 void *xinfo_dummy;
 IsoNode *node;
 IsoStream *stream;

 if(ftest == NULL)
   return(1);

 node= (IsoNode *) node_pt;
 arg1= ftest->arg1;
 arg2= ftest->arg2;

 if(node == NULL) {
   if(ftest->test_type > 2 && ftest->test_type != 4) {
     value= 0;
     goto ex;
   }
 }

 switch(ftest->test_type) {
 case 0: /* -false */
   value= 0;

 break; case 1: /* -name *arg1 (regex in *arg2) */
   ret= regexec(arg2, name, 1, &name_match, 0);
   value= !ret;

 break; case 2: /* -type *arg1 */
   value= 1;
   ft= *((char *) arg1);
   if(ft!=0) {
     if(S_ISBLK(stbuf->st_mode)) {
       if(ft!='b')
         value= 0;
     } else if(S_ISCHR(stbuf->st_mode)) {
       if(ft!='c')
         value= 0;
     } else if(S_ISDIR(stbuf->st_mode)) {
       if(ft=='m') {
         if(node != NULL)
           value= 0;
         else if(boss_stbuf==NULL)
           value= 0;
         else if(boss_stbuf->st_dev == stbuf->st_dev)
           value= 0;
       } else if(ft!='d')
         value= 0;
     } else if(S_ISFIFO(stbuf->st_mode)) {
       if(ft!='p')
         value= 0;
     } else if(S_ISREG(stbuf->st_mode)) {
       if(ft!='f' && ft!='-')
         value= 0;
     } else if(((stbuf->st_mode)&S_IFMT)==S_IFLNK) {
       if(ft!='l')
         value= 0;
     } else if(((stbuf->st_mode)&S_IFMT)==S_IFSOCK) {
       if(ft!='s')
         value= 0;
     } else if((flag & 1) && ((stbuf->st_mode) & S_IFMT) == Xorriso_IFBOOT) {
       if(ft!='e' || node == NULL)
         value= 0;
     } else {
       if(ft!='X')
         value= 0;
     }
   }

 break; case 3: /* -damaged */;
   value= Xorriso_file_eval_damage(xorriso, node, &damage_start, &damage_end,
                                   0);
   if(value > 0)
     value= 1;

 break; case 4: /* -lba_range *arg1 *arg2 */
   value= 1;
   start_lba= *((int *) ftest->arg1);
   end_lba= *((int *) ftest->arg2);
   if(node == NULL) {
     value= !(start_lba >= 0);
     goto ex;
   }
   ret= Xorriso__start_end_lbas(node, &lba_count,
                                &file_start_lbas, &file_end_lbas, &size, 0);
   if(ret <= 0) {
     if(ret < 0)
       Xorriso_process_msg_queues(xorriso, 0);
     if(start_lba >= 0)
       value= 0;
   } else {
     for(i= 0; i < lba_count; i++) {
       if(start_lba >= 0) {
         if(file_end_lbas[i] < start_lba || file_start_lbas[i] > end_lba)
           value= 0;
       } else {
         if(file_end_lbas[i] >= -start_lba && file_start_lbas[i] <= -end_lba)
           value= 0;
       }
     }
   }

 break; case 5: /* -has_acl */
   ret = Xorriso_getfacl(xorriso, (void *) node, "", NULL, 2);
   if(ret <= 0) {
     value= -1;
     Xorriso_process_msg_queues(xorriso, 0);
     goto ex;
   }
   value= (ret == 1);

 break; case 6: /* -has_xattr */
        case 14: /* -has_any_xattr */
   ret = Xorriso_getfattr(xorriso, (void *) node, "", NULL,
                          64 | (8 * (ftest->test_type == 14)));
   if(ret < 0) {
     value= -1;
     Xorriso_process_msg_queues(xorriso, 0);
     goto ex;
   }
   value= (ret > 0);

 break; case 7: /* -has_aaip */
   ret= iso_node_get_xinfo(node, aaip_xinfo_func, &xinfo_dummy);
   if(ret < 0) {
     value= -1;
     Xorriso_process_msg_queues(xorriso, 0);
     goto ex;
   }
   value= (ret > 0);

 break; case 8: /* -has_filter */
   value= 0;
   if(LIBISO_ISREG(node)) {
     stream= iso_file_get_stream((IsoFile *) node);
     if(iso_stream_get_input_stream(stream, 0) != NULL)
       value= 1;
   }

 break; case 9: /* -wanted_node arg1 (for internal use) */
   value= (((IsoNode *) arg1) == node);

 break; case 10: /* -pending_data */
   value= 1;
   if(!LIBISO_ISREG(node)) {
     value= 0;
   } else {
     ret= Xorriso__file_start_lba(node, &start_lba, 0);
     if(ret > 0 && start_lba >= 0)
       value= 0;
   }

 break; case 11: /* -decision */
   value= 2;
   decision= (char *) arg1;
   if(strcmp(decision, "yes") == 0 || strcmp(decision, "true") == 0)
     value= 3;

 break; case 12: /* -prune */
   value= 1;
   ftest->boss->prune= 1;

 break; case 13: /* -wholename *arg1 (regex in *arg2) */
   ret= regexec(arg2, path, 1, &name_match, 0);
   value= !ret;

 break; case 15: /* -has_md5 */
   ret= Xorriso_get_md5(xorriso, node, path, md5, 1);
   value= (ret > 0);

 break; case 16: /* -disk_name *arg1 (regex in *arg2) */
   value= !! Exprtest_match_disk_name(xorriso, ftest, node, 0);

 break; case 17: /* -hidden int *arg1 */
   value= 0;
   ret= iso_node_get_hidden(node);
   mask= *((int *) arg1) & 3;
   if(mask == 0 && !(ret & (LIBISO_HIDE_ON_RR | LIBISO_HIDE_ON_JOLIET)))
     value= 1;
   else if(mask == 1 && (ret & LIBISO_HIDE_ON_RR))
     value= 1;
   else if(mask == 2 && (ret & LIBISO_HIDE_ON_JOLIET))
     value= 1;
   else if(mask == 3 && (ret & LIBISO_HIDE_ON_RR) &&
                        (ret & LIBISO_HIDE_ON_JOLIET))
     value= 1;

 break; default:

   /* >>> complain about unknown test type */;

   value= -1;

 }

ex:;
 if(ftest->invert && value<=1 && value>=0)
   value= !value;
 if(file_start_lbas != NULL)
   free((char *) file_start_lbas);
 if(file_end_lbas != NULL)
   free((char *) file_end_lbas);
 return(value);
}


/* @return <0 = error , 0 = no match , 1 = match */
int Xorriso_findi_test(struct XorrisO *xorriso, struct FindjoB *job,
                       IsoNode *node, char *name, char *path,
                       struct stat *boss_stbuf, struct stat *stbuf,
                       int depth, int flag)
{
 int ret;

 job->prune= 0;
 ret= Findjob_test_2(xorriso, job, node, name, path, boss_stbuf, stbuf, 1);
 if(ret <= 0)
   return(ret);
 return(1);
}


int Xorriso_findi_headline(struct XorrisO *xorriso, struct FindjoB *job,
                           int flag)
{
 int action;

 action= Findjob_get_action(job, 0);
 if(action == 21) {                                         /* report_damage */
   sprintf(xorriso->result_line, "Report layout: %8s , %8s , %8s , %s\n",
           "at byte", "Range", "Filesize", "ISO image path");
   Xorriso_result(xorriso, 0);
 } else if(action == 22) {                                     /* report_lba */
   sprintf(xorriso->result_line,
           "Report layout: %2s , %8s , %8s , %8s , %s\n",
           "xt", "Startlba", "Blocks", "Filesize", "ISO image path");
   Xorriso_result(xorriso, 0);
 }
 return(1);
}


/* @param flag bit0= recursion
               bit1= do not count deleted files with rm and rm_r
               bit2= do not dive into split file directories
                     (implicitly given with actions 14=compare and 17=update)
   @return <=0 error, 1= ok , 2= dir node and path has been deleted
*/
int Xorriso_findi(struct XorrisO *xorriso, struct FindjoB *job,
                  void *boss_iter, off_t boss_mem,
                  void *dir_node_generic, char *dir_path,
                  struct stat *dir_stbuf, int depth, int flag)
{
 int ret, action= 0, hflag, deleted= 0, no_dive= 0;
 IsoDirIter *iter= NULL;
 IsoDir *dir_node= NULL;
 IsoNode *node, *iso_node;
 IsoImage *volume= NULL;
 struct stat stbuf;
 char *name;
 off_t mem;
 IsoNode **node_array= NULL;
 int node_count, node_idx;
 char *path= NULL, *abs_path= NULL;

 if(xorriso->request_to_abort)
   {ret= 0; goto ex;}

 path= malloc(SfileadrL);
 abs_path= malloc(SfileadrL);
 if(path==NULL || abs_path==NULL) {
   Xorriso_no_malloc_memory(xorriso, &path, 0);
   {ret= -1; goto ex;}
 }

 action= Findjob_get_action(job, 0);
 if(action<0)
   action= 0;
 if(!(flag & 1))
   Xorriso_findi_headline(xorriso, job, 0);

 dir_node= (IsoDir *) dir_node_generic;
 if(dir_node==NULL) {
   ret= Xorriso_get_volume(xorriso, &volume, 0);
   if(ret<=0)
     {ret= -1; goto ex;}
   ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, dir_path, path, 1|2|4);
   if(ret<=0)
     goto ex;
   ret= Xorriso_node_from_path(xorriso, volume, path, &iso_node, 0);
   dir_node= (IsoDir *) iso_node;
   if(ret<=0)
     {ret= 0; goto ex;}
   ret= Xorriso_fake_stbuf(xorriso, "", dir_stbuf, &iso_node, 1);
   if(ret<=0)
     goto ex;

   name= strrchr(dir_path, '/');
   if(name==NULL)
     name= dir_path;
   else
     name++;

   ret= Xorriso_findi_test(xorriso, job, iso_node, name, path, NULL, dir_stbuf,
                           depth, 0);
   if(ret<0)
     goto ex;
   if(job->prune)
     no_dive= 1;
   if(ret>0) {
     iso_node_ref(iso_node); /* protect from real disposal */
     ret= Xorriso_findi_action(xorriso, job,
                               (IsoDirIter *) boss_iter, boss_mem,
                               path, dir_path, iso_node, depth,
                               flag&(1|2));
     deleted= (iso_node_get_parent(iso_node) == NULL); /* still in tree ? */
     iso_node_unref(iso_node); /* eventually do real disposal */
     if(ret<=0)
       goto ex;
     if(xorriso->request_to_abort)
       {ret= 0; goto ex;}
     if(ret==2 || deleted) {
       /* re-determine dir_node in case it has a new persona */ 
       ret= Xorriso_node_from_path(xorriso, volume, path, &iso_node, 1);
       if(ret==0) {
         deleted= 1;
         {ret= 2; goto ex;}
       }
       if(ret<0)
         {ret= 0; goto ex;}
       dir_node= (IsoDir *) iso_node;
       ret= Xorriso_fake_stbuf(xorriso, "", dir_stbuf, &iso_node, 1);
       if(ret<=0)
         goto ex;
     }
     if(ret==3)
       no_dive= 1;
   }
 }
 if(no_dive || !LIBISO_ISDIR((IsoNode *) dir_node))
   {ret= 1; goto ex;}
 if(action == 14 || action == 17 || (flag & 4))
   if(Xorriso_is_split(xorriso, dir_path, (IsoNode *) dir_node, 1)>0)
     {ret= 1; goto ex;}

 mem= boss_mem;
 hflag= 1;
 if(action==1 || action==2 || action==3 || action==17 || action == 28 ||
    action == 32)
   hflag|= 2; /* need freedom to manipulate image */
 if(action==14 || action==17 || action == 28 || action == 35 || action == 36)
   hflag|= 4; /* need LBA sorted iteration for good data reading performance */
 ret= Xorriso_findi_iter(xorriso, dir_node, &mem,
                         &iter, &node_array, &node_count, &node_idx,
                         &node, hflag);
 if(ret<=0)
   goto ex;
 while(1) {
   ret= Xorriso_findi_iter(xorriso, dir_node, &mem, &iter,
                           &node_array, &node_count, &node_idx, &node, 0);
   if(ret<0)
     goto ex;
   if(ret==0)
 break;
   name= (char *) iso_node_get_name(node);
   ret= Xorriso_make_abs_adr(xorriso, dir_path, name, path, 4);
   if(ret<=0)
     goto ex;
   ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1);
   if(ret<0)
     goto ex;
   if(ret==0)
 continue;

/* ??? This seems to be redundant with the single test above
   ??? Should i dive in unconditionally and leave out test and action here ?
   ??? Then do above test unconditionally ?
   --- Seems that the current configuration represents the special
       handling of the find start path with mount points. Dangerous to change.
*/

   ret= Xorriso_findi_test(xorriso, job, node, name, path, dir_stbuf, &stbuf,
                           depth, 0);
   if(ret<0)
     goto ex;
   if(job->prune)
     no_dive= 1;
   if(ret>0) {
     ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, path, abs_path, 1|2|4);
     if(ret<=0)
       goto ex;
     ret= Xorriso_findi_action(xorriso, job, iter, mem,
                               abs_path, path, node, depth, 1|(flag&2));
     if(xorriso->request_to_abort)
       {ret= 0; goto ex;}
     if(ret==2) { /* node has been deleted */
       /* re-determine node in case it has a new persona */ 
       if(volume==NULL) {
         ret= Xorriso_get_volume(xorriso, &volume, 0);
         if(ret<=0)
           {ret= -1; goto ex;}
       }
       ret= Xorriso_node_from_path(xorriso, volume, abs_path, &node, 1);
       if(ret==0)
 continue;
       if(ret<0)
         {ret= 0; goto ex;}
       ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1);
       if(ret<0)
         goto ex;
       if(ret==0)
 continue;
     }
     no_dive= (ret==3);
     if(ret<=0) {
       if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0)
         goto ex;
     }
   }

   if(S_ISDIR(stbuf.st_mode) && !no_dive) {
     ret= Xorriso_findi(xorriso, job, (void *) iter, mem,
                        (void *) node, path, &stbuf, depth+1, flag|1);
     if(ret<0)
       goto ex;
   }
 }

 ret= 1;
ex:;
 if(path!=NULL)
   free(path);
 if(abs_path!=NULL)
   free(abs_path);
 Xorriso_process_msg_queues(xorriso,0);

 Xorriso_findi_iter(xorriso, dir_node, &mem, &iter, &node_array, &node_count,
                    &node_idx, &node, (1<<31));
 if(ret<=0)
   return(ret);
 if(deleted)
   return(2);
 return(1);
}


/* @param flag bit0= do not dive into trees
               bit1= do not perform job->action on resulting node array
               bit2= do not free node_array after all actions are done
*/
int Xorriso_findi_sorted(struct XorrisO *xorriso, struct FindjoB *job,
                         off_t boss_mem, int filec, char **filev, int flag)
{
 int i, ret, find_flag= 0;
 struct FindjoB array_job, *proxy_job= NULL, *hindmost= NULL, *hmboss= NULL;
 struct stat dir_stbuf;
 IsoNode *node;
 char abs_path[SfileadrL];
 off_t mem_needed= 0;

 array_job.start_path= NULL;

 if(job->action == 14 || job->action == 17)
   find_flag|= 4;
 if(job->action>=9 && job->action<=13) { /* actions which have own findjobs */
   /* array_job replaces the hindmost job in the chain */
   for(hindmost= job; hindmost->subjob != NULL; hindmost= hindmost->subjob)
     hmboss= hindmost;
   if(hmboss == NULL)
     {ret= -1; goto ex;}
   memcpy(&array_job, hindmost, sizeof(struct FindjoB));
   hmboss->subjob= &array_job;
   proxy_job= job;
 } else {
   memcpy(&array_job, job, sizeof(struct FindjoB));
   proxy_job= &array_job;
   hindmost= job;
 }
 array_job.start_path= NULL; /* is owned by the original, not by array_job */

 /* Count matching nodes */
 Xorriso_destroy_node_array(xorriso, 0);
 array_job.action= 30; /* internal: count */
 for(i= 0; i < filec; i++) {
   if(flag & 1) {
     xorriso->node_counter++;
 continue;
   }
   ret= Findjob_set_start_path(proxy_job, filev[i], 0);
   if(ret <= 0)
     goto ex;
   ret= Xorriso_findi(xorriso, proxy_job, NULL, boss_mem, NULL,
                      filev[i],  &dir_stbuf, 0, find_flag);
   if(ret <= 0)
     goto ex;
 }
 if(xorriso->node_counter <= 0)
   {ret= 1; goto ex;}

 mem_needed= boss_mem + xorriso->node_counter * sizeof(IsoNode *);
 if(!(flag &1)) {
   ret= Xorriso_check_temp_mem_limit(xorriso, mem_needed, 0);
   if(ret <= 0) {
     /* Memory curbed : Perform unsorted find jobs */
     if(hmboss != NULL)
       hmboss->subjob= hindmost;
     for(i= 0; i < filec; i++) {
       ret= Findjob_set_start_path(job, filev[i], 0);
       if(ret <= 0)
         goto ex;
       ret= Xorriso_findi(xorriso, job, NULL, boss_mem, NULL,
                          filev[i], &dir_stbuf, 0, find_flag);
       if(ret <= 0)
         if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0)
           goto ex;
     }
     {ret= 1; goto ex;}
   }
 }

 /* Copy matching nodes into allocated array */
 ret= Xorriso_new_node_array(xorriso, xorriso->temp_mem_limit, 0, 0);
 if(ret <= 0)
   goto ex;
 array_job.action= 31; /* internal: register */
 xorriso->node_counter= 0;
 for(i= 0; i < filec; i++) {
   if(flag & 1) {
     ret= Xorriso_get_node_by_path(xorriso, filev[i], NULL, &node, 0);
     if(ret <= 0)
       goto ex;
     if(xorriso->node_counter < xorriso->node_array_size) {
       xorriso->node_array[xorriso->node_counter++]= (void *) node;
       iso_node_ref(node);
     }
 continue;
   }
   ret= Findjob_set_start_path(proxy_job, filev[i], 0);
   if(ret <= 0)
     goto ex;
   ret= Xorriso_findi(xorriso, proxy_job, NULL, mem_needed, NULL,
                      filev[i], &dir_stbuf, 0, find_flag);
   if(ret <= 0)
     goto ex;
 }

 Xorriso_sort_node_array(xorriso, 0);
 if(flag & 2)
   {ret= 1; goto ex;}

 /* Perform job->action on xorriso->node_array */

 /* Headlines of actions report_damage , report_lba */;
 Xorriso_findi_headline(xorriso, job, 0);

 for(i= 0; i < xorriso->node_counter; i++) {
   node= xorriso->node_array[i];
   ret= Xorriso_path_from_node(xorriso, node, abs_path, 0);
   if(ret < 0)
     goto ex;
   if(ret == 0)
 continue; /* node is deleted from tree meanwhile */

   ret= Xorriso_findi_action(xorriso, hindmost, NULL, (off_t) 0,
                             abs_path, abs_path, node, 0, 1);
   if(ret <= 0 || xorriso->request_to_abort)
     if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0)
       goto ex;
 }

 ret= 1;
ex:;
 if(!(flag & (2 | 4)))
   Xorriso_destroy_node_array(xorriso, 0);
 if(hmboss != NULL)
   hmboss->subjob= hindmost;
 if(array_job.start_path != NULL)
   free(array_job.start_path);
 return(ret);
}


int Xorriso_all_node_array(struct XorrisO *xorriso, int addon_nodes, int flag)
{  
 int ret;
 struct FindjoB *job= NULL;
 struct stat dir_stbuf;

 ret= Findjob_new(&job, "/", 0);
 if(ret<=0) {
   Xorriso_no_findjob(xorriso, "xorriso", 0);
   {ret= -1; goto ex;}
 }
 Findjob_set_action_target(job, 30, NULL, 0);
 Xorriso_destroy_node_array(xorriso, 0);
 ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/",
                    &dir_stbuf, 0, 0);
 if(ret <= 0)
   goto ex;
 ret= Xorriso_new_node_array(xorriso, xorriso->temp_mem_limit, addon_nodes, 0);
 if(ret <= 0)
   goto ex;
 Findjob_set_action_target(job, 31, NULL, 0);
 ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/",
                    &dir_stbuf, 0, 0);
 if(ret <= 0)
   goto ex;
 ret= 1;
ex:;
 Findjob_destroy(&job, 0);
 return(ret);
}


int Xorriso_perform_acl_from_list(struct XorrisO *xorriso, char *file_path,
                                  char *uid, char *gid, char *acl, int flag)
{
 int ret, zero= 0;
 uid_t uid_number;
 gid_t gid_number;

 /* Set group and owner */
 if(gid[0]) {
   ret= Xorriso_convert_gidstring(xorriso, gid, &gid_number, 0);
   if(ret<=0)
     return(ret);
   ret= Xorriso_set_gid(xorriso, file_path, gid_number, 0);
   if(ret<=0)
     return(ret);
 }
 if(uid[0]) {
   ret= Xorriso_convert_uidstring(xorriso, uid, &uid_number, 0);
   if(ret<=0)
     return(ret);
   ret= Xorriso_set_uid(xorriso, file_path, uid_number, 0);
   if(ret<=0)
     return(ret);
 }
 ret= Xorriso_option_setfacli(xorriso, acl, 1, &file_path, &zero, 0);
 if(ret <= 0)
   return(ret);
 return(1);
}


/*
  @param flag   bit0= do not perform setfattr but only check input
*/
int Xorriso_path_setfattr(struct XorrisO *xorriso, void *in_node, char *path,
                        char *name, size_t value_length, char *value, int flag)
{
 int ret, hflag;
 size_t num_attrs= 1;
 char *name_pt;

 hflag= 2;
 name_pt= name;
 if(name[0] == 0) {
   sprintf(xorriso->info_text,
           "-setfattr: Empty attribute name is not allowed");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
   return(0);
 } else if(strcmp(name, "--remove-all") == 0) {
   if(value[0]) {
     sprintf(xorriso->info_text,
             "-setfattr: Value is not empty with pseudo name --remove-all");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
     return(0);
   }
   num_attrs= 0;
   hflag= 0;
 } else if(name[0] == '-') {
   name_pt++;
   hflag|= 4;
 } else if(name[0] == '=' || name[0] == '+') {
   name_pt++;
 }
 if(flag & 1)
   return(1);
 ret= Xorriso_setfattr(xorriso, in_node, path,
                       num_attrs, &name_pt, &value_length, &value, hflag);
 return(ret);
}


/* Warning: The text content of lst gets mangled by 0s and unescaping.
*/
int Xorriso_perform_attr_from_list(struct XorrisO *xorriso, char *path,
                      struct Xorriso_lsT *lst_start, int flag)
{
 int ret, eaten;
 char *valuept, *ept, *line, **names= NULL, **values= NULL;
 size_t num_attr= 0, *value_lengths= NULL, v_len;
 struct Xorriso_lsT *lst;

 for(lst= lst_start; lst != NULL; lst= Xorriso_lst_get_next(lst, 0))
   num_attr++;
 if(num_attr == 0) {
   ret= Xorriso_setfattr(xorriso, NULL, path, num_attr, NULL, NULL, NULL, 0);
   goto ex;
 }

 names= calloc(num_attr, sizeof(char *));
 if(names == NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   ret= -1; goto ex;
 }
 value_lengths= calloc(num_attr, sizeof(size_t));
 if(value_lengths== NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   ret= -1; goto ex;
 }
 values= calloc(num_attr, sizeof(char *));
 if(values== NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   ret= -1; goto ex;
 }

 num_attr= 0;
 for(lst= lst_start; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) {
   line= Xorriso_lst_get_text(lst, 0);
   ept= strchr(line, '=');
   if(ept == NULL)
 continue;
   /* Split into name and content */;
   *ept= 0;
   valuept= ept + 1;

   /* Strip quotes from value */
   v_len= strlen(valuept);
   if(v_len < 2 || *valuept != '"' || *(valuept + v_len - 1) != '"')
 continue;
   *valuept= 0;
   *(valuept + v_len - 1)= 0;
   valuept++;
   v_len-= 2;

   /* Unescape backslashes , values eventually with 0-bytes */
   ret= Sfile_bsl_interpreter(line, strlen(line), &eaten, 0);
   if(ret <= 0)
 continue;
   ret= Sfile_bsl_interpreter(valuept, (int) v_len, &eaten, 2);
   if(ret <= 0)
 continue;

   names[num_attr]= line;
   values[num_attr]= valuept;
   value_lengths[num_attr]= v_len - eaten;
   num_attr++;
 }
 ret= Xorriso_setfattr(xorriso, NULL, path, num_attr, names,
                       value_lengths, values, 0);
ex:;
 if(names != NULL)
   free(names);
 if(value_lengths != NULL)
   free(value_lengths);
 if(values != NULL)
   free(values);
 return(ret);
}

