
/* 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 the implementations of classes:

   - SplitparT which represents byte intervals of data files.

   - DirseQ which crawls along a directory's content list.

   - ExclusionS which manages the list of excluded file paths and
     leaf patterns.
     Because of its structural identity it is also used for disk address
     oriented hiding at insert time as of mkisofs.

   - Xorriso_lsT which provides a generic double-linked list.

   - LinkiteM, PermiteM which temporarily record relations and states.

*/

#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 <utime.h>
#include <dirent.h>
#include <errno.h>


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


/* ---------------------------- SplitparT ------------------------- */


struct SplitparT {
 char *name;
 int partno;
 int total_parts;
 off_t offset;
 off_t bytes;
 off_t total_bytes;
};

static char Splitpart_wordS[][16]= {"part_", "_of_", "_at_", "_with_", "_of_"};


int Splitparts_new(struct SplitparT **o, int count, int flag)
{
 int i;

 (*o)= TSOB_FELD(struct SplitparT, count);
 if((*o)==NULL)
   return(-1);
 for(i= 0; i<count; i++) {
   (*o)[i].name= NULL;
   (*o)[i].partno= 0;
   (*o)[i].total_parts= 0;
   (*o)[i].offset= 0;
   (*o)[i].bytes= 0;
   (*o)[i].total_bytes= 0;
 }
 return(1);
}


int Splitparts_destroy(struct SplitparT **o, int count, int flag)
{
 int i;

 if((*o)==NULL)
   return(0);
 for(i= 0; i<count; i++) {
   if((*o)[i].name!=NULL)
     free((*o)[i].name);
 }
 free(*o);
 *o= NULL;
 return(1);
}


int Splitparts_set(struct SplitparT *o, int idx,
                   char *name, int partno, int total_parts,
                   off_t offset, off_t bytes, off_t total_bytes, int flag)
{
 if(o[idx].name!=NULL)
   free(o[idx].name);
 o[idx].name= strdup(name);
 if(o[idx].name==NULL)
   return(-1);
 o[idx].partno= partno;
 o[idx].total_parts= total_parts;
 o[idx].offset= offset;
 o[idx].bytes= bytes;
 o[idx].total_bytes= total_bytes;
 return(1);
}


int Splitparts_get(struct SplitparT *o, int idx, char **name, int *partno,
                   int *total_parts, off_t *offset, off_t *bytes,
                   off_t *total_bytes, int flag)
{
 *name= o[idx].name;
 *partno= o[idx].partno;
 *total_parts= o[idx].total_parts;
 *offset= o[idx].offset;
 *bytes= o[idx].bytes;
 *total_bytes= o[idx].total_bytes;
 return(1);
}


int Splitpart__read_next_num(char *base_pt, char **next_pt, off_t *num,
                             int flag)
{
 char *cpt, *ept, scale[4];
 double sfak;

 *num= 0;
 for(cpt= base_pt; *cpt!=0 && !isdigit(*cpt); cpt++);
 if(*cpt==0)
   return(0);
 for(ept= cpt; *ept!=0 && isdigit(*ept); ept++)
   *num= (*num)*10+(*ept)-'0';
 scale[0]= '1';
 scale[1]= *ept;
 scale[2]= 0;
 sfak= Scanf_io_size(scale, 0);
 *num *= (off_t) sfak;
 if(sfak > 1.0)
   ept++;
 *next_pt= ept;
 return(1);
}


int Splitpart__parse(char *name, int *partno, int *total_parts,
                    off_t *offset, off_t *bytes, off_t *total_bytes, int flag)

{
 int ret; 
 off_t num;
 char *cpt, *ept;

 cpt= name;
 if(strncmp(cpt, Splitpart_wordS[0], strlen(Splitpart_wordS[0])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, &num, 0);
 if(ret<=0)
   return(ret);
 *partno= num;
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[1], strlen(Splitpart_wordS[1])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, &num, 0);
 if(ret<=0)
   return(ret);
 *total_parts= num;
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[2], strlen(Splitpart_wordS[2])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, offset, 0);
 if(ret<=0)
   return(ret);
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[3], strlen(Splitpart_wordS[3])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, bytes, 0);
 if(ret<=0)
   return(ret);
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[4], strlen(Splitpart_wordS[4])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, total_bytes, 0);
 if(ret<=0)
   return(ret);
 if(*ept != 0)
   return(0);
 return(1);
}


int Splitpart__is_part_path(char *path, int flag)
{
 int partno, total_parts, ret;
 off_t offset, bytes, total_bytes;
 char *name;

 name= strrchr(path, '/');
 if(name == NULL)
   name= path;
 else
   name++;
 ret= Splitpart__parse(name, &partno, &total_parts, &offset, &bytes,
                       &total_bytes, 0);
 return(ret > 0);
}


/* part_#_of_#_at_#_with_#_of_#
*/
int Splitpart__compose(char *adr, int partno, int total_parts,
                       off_t offset, off_t bytes, off_t total_bytes, int flag)
{
 sprintf(adr, "%s%d%s%d%s", Splitpart_wordS[0], partno, Splitpart_wordS[1],
                            total_parts, Splitpart_wordS[2]);
 if((offset % (1024*1024))==0 && offset>0) {
   Sfile_off_t_text(adr+strlen(adr), offset / (1024*1024), 0);
   strcat(adr, "m");
 } else
   Sfile_off_t_text(adr+strlen(adr), offset, 0);
 strcat(adr, Splitpart_wordS[3]); 
 if((bytes % (1024*1024))==0) {
   Sfile_off_t_text(adr+strlen(adr), bytes / (1024*1024), 0);
   strcat(adr, "m");
 } else
   Sfile_off_t_text(adr+strlen(adr), bytes, 0);
 strcat(adr, Splitpart_wordS[4]);
 Sfile_off_t_text(adr+strlen(adr), total_bytes, 0);
 return(1);
}


int Splitparts_cmp(const void *v1, const void *v2)
{
 struct SplitparT *p1, *p2;

 p1= (struct SplitparT *) v1;
 p2= (struct SplitparT *) v2;
 
 if(p1->partno>p2->partno)
   return(1);
 if(p1->partno<p2->partno)
   return(-1);
 if(p1->offset>p2->offset)
   return(1);
 if(p1->offset<p2->offset)
   return(-1);
 return(0);
}


int Splitparts_sort(struct SplitparT *o, int count, int flag)
{
 qsort(o, (size_t) count, sizeof(struct SplitparT), Splitparts_cmp);
 return(1);
}


/* ---------------------------- End SplitparT ------------------------- */


/* ------------------------------ DirseQ  ------------------------------ */


static int Dirseq_buffer_sizE= 100;

struct DirseQ {
  char adr[SfileadrL];
  DIR *dirpt;
  int count;  
  char **buffer;
  int buffer_size;
  int buffer_fill;
  int buffer_rpt;

  struct DirseQ *next;
};

int Dirseq_destroy(struct DirseQ **o, int flag);
int Dirseq_next_adrblock(struct DirseQ *o, char *replies[], int *reply_count,
                         int max_replies, int flag);


int Dirseq_new(struct DirseQ **o, char *adr, int flag)
/*
 bit0= with non-fatal errors do not complain about failed opendir() 
*/
{
 int ret,i,severe_error;
 struct DirseQ *m;

 m= *o= TSOB_FELD(struct DirseQ,1);
 if(m==NULL)
   return(-1);
 m->adr[0]= 0;
 m->dirpt= NULL;
 m->count= 0;
 m->buffer= NULL;
 m->buffer_size= 0;
 m->buffer_fill= 0;
 m->buffer_rpt= 0;
 m->next= NULL;
 if(Sfile_str(m->adr, adr, 0)<=0)
   {ret= 0; goto failed;}
 m->buffer= TSOB_FELD(char *,Dirseq_buffer_sizE);
 if(m->buffer==NULL)
   {ret= -1; goto failed;}
 m->buffer_size= Dirseq_buffer_sizE;
 for(i= 0;i<m->buffer_size;i++)
   m->buffer[i]= NULL;
 if(adr[0]==0)
   m->dirpt= opendir(".");
 else
   m->dirpt= opendir(adr);
 if(m->dirpt==NULL) {
   severe_error= (errno && errno!=ENOENT && errno!=EACCES && errno!=ENOTDIR);
   if(severe_error || !(flag&1))
     fprintf(stderr,"opendir(%s) failed : %s\n",adr,strerror(errno));
   ret= -severe_error;
   goto failed;
 }
 return(1);
failed:;
 Dirseq_destroy(o,0);
 return(ret);
}


int Dirseq_destroy(struct DirseQ **o, int flag)
{
 int i;

 if(*o==NULL)
   return(0);
 if((*o)->dirpt!=NULL)
   closedir((*o)->dirpt);
 if((*o)->buffer!=NULL) {
   for(i=0;i<(*o)->buffer_size;i++)
     if((*o)->buffer[i]!=NULL)
       free((*o)->buffer[i]);
   free((char *) (*o)->buffer);
 }
 free((char *) *o);
 (*o)= NULL;
 return(1);
}


int Dirseq_set_next(struct DirseQ *o, struct DirseQ *next, int flag)
{
 o->next= next;
 return(1);
}


int Dirseq_get_next(struct DirseQ *o, struct DirseQ **next, int flag)
{
 *next= o->next;
 return(1);
}


int Dirseq_get_adr(struct DirseQ *o, char **adrpt, int flag)
{
 *adrpt= o->adr;
 return(1);
}


int Dirseq_rewind(struct DirseQ *o, int flag)
{
 rewinddir(o->dirpt);
 return(1);
}


int Dirseq_next_adr(struct DirseQ *o, char reply[SfileadrL], int flag)
/*
flag:
 bit0= permission to use buffer
 bit1= do not increment counter
 bit2= ignore buffer in any case
 bit3= do not exclude '.' and '..'
 bit4= sort buffer
 bit5= sort only incomplete last buffer
return:
 <0 error
 0= no more entries available
 1= ok, reply is valid
*/
{
 int ret;
 struct dirent *entry;
 char *name;

 static int override_flag_0= 0,override_flag_1= 32;
 flag= (flag&~override_flag_0)|override_flag_1;

 if((flag&1) && o->buffer_rpt>=o->buffer_fill) {
   /* permission to buffer and buffer empty : load a buffer */
   ret= Dirseq_next_adrblock(o,o->buffer,&(o->buffer_fill),
                             o->buffer_size,2|4|(flag&16));
   if(ret<=0)
     return(ret);
   o->buffer_rpt= 0;
   if((flag&32) && o->buffer_fill<o->buffer_size && o->buffer_fill>0) 
     Sort_argv(o->buffer_fill,o->buffer,0);
 }
 if(o->buffer_rpt<o->buffer_fill && !(flag&4)) {
   ret= Sfile_str(reply,o->buffer[o->buffer_rpt],0);
   Sregex_string(&(o->buffer[o->buffer_rpt]),NULL,0);
   if(ret<=0)
     return(-1);
   (o->buffer_rpt)++;
   if(!(flag&2))
     o->count++;
   return(1);
 }
 do {
   entry= readdir(o->dirpt);
   if(entry==NULL) {
     /* >>> how to distinguish error from EOF , do i need a (FILE *) ? */
     return(0);
   }
   if(strlen(entry->d_name)>=SfileadrL) {
     fprintf(stderr,"--- oversized directory entry (number %d) :\n    %s",
                    o->count+1,entry->d_name);
     return(-1);
   }
   name= entry->d_name;
   if(flag&8)
 break;
   /* skip "." and ".." */
 } while(name[0]=='.' && ((name[1]=='.' && name[2]==0) || name[1]==0));
 if(Sfile_str(reply,name,0)<=0)
   return(-1);
 if(!(flag&2))
   o->count++;
 return(1);
}


int Dirseq_next_adrblock(struct DirseQ *o, char *replies[], int *reply_count,
                         int max_replies, int flag)
/* @param replies A vector of Sregex_string pointers */
/*
flag:
 bit0= permission to use buffer
 bit1= do not increment counter
 bit2= ignore buffer in any case
 bit4= sort replies
return:
 <0 error
 0= no more entries available
 1= ok, reply is valid
*/
{
 int i,ret;
 char reply[SfileadrL];

 *reply_count= 0;
 for(i=0;i<max_replies;i++) {
   ret= Dirseq_next_adr(o,reply,flag&(1|2|4));
   if(ret<0)
     return(ret);
   if(ret==0)
 break;
   if(Sregex_string(&(replies[i]),reply,0)<=0)
     return(-1);
   (*reply_count)++;
 }
 if((*reply_count)==0)
   return(0);
 if(flag&16) 
   Sort_argv(*reply_count,replies,0);
 return(1);
}


/* ---------------------------- End DirseQ  ----------------------------- */


/* ---------------------------- Xorriso_lsT ----------------------------- */


/*
 @param flag Bitfield for control purposes
        bit0= insert before link rather than after it
        bit1= do not copy data (e.g. because *data is invalid)
        bit2= attach data directly by pointer rather than by copying
*/
int Xorriso_lst_new_binary(struct Xorriso_lsT **lstring, char *data,
                           int data_len, struct Xorriso_lsT *link, int flag)
{
 int ret;
 struct Xorriso_lsT *s;

 s= TSOB_FELD(struct Xorriso_lsT,1);
 if(s==NULL)
   return(-1);
 s->text= NULL;
 s->next= s->prev= NULL;

 if(flag & 4) {
   s->text= data;
 } else {
   if(data_len<=0)
     {ret= -1; goto failed;}
   s->text= Smem_malloC(data_len);
   if(s->text==NULL)
     {ret= -1; goto failed;}
   if(!(flag&2))
     memcpy(s->text,data,data_len);
 }

 if(link==NULL) {
   ;
 } else if(flag&1) {
   s->next= link;
   s->prev= link->prev;
   if(link->prev!=NULL) 
     link->prev->next= s;
   link->prev= s;
 } else {
   s->prev= link;
   s->next= link->next;
   if(link->next!=NULL) 
     link->next->prev= s;
   link->next= s;
 }
 *lstring= s;
 return(1);
failed:;
 *lstring= s;
 Xorriso_lst_destroy(lstring,0);
 return(-1);
}


/*
 @param flag Bitfield for control purposes
             see Xorriso_lst_new_binary()
*/
int Xorriso_lst_new(struct Xorriso_lsT **lstring, char *text,
                    struct Xorriso_lsT *link, int flag)
{
 int ret;

 ret= Xorriso_lst_new_binary(lstring,text,strlen(text)+1,link,flag);
 return(ret);
}


/*
 @param flag Bitfield for control purposes
             bit0= do not set *lstring to NULL
*/
int Xorriso_lst_destroy(struct Xorriso_lsT **lstring, int flag)
{
 struct Xorriso_lsT *s;

 s= *lstring;
 if(s==NULL)
   return(0);
 if(s->prev!=NULL)
   s->prev->next= s->next;
 if(s->next!=NULL)
   s->next->prev= s->prev;
 if(s->text!=NULL)
   Smem_freE(s->text);
 Smem_freE((char *) s);
 if(!(flag&1))
   *lstring= NULL;
 return(1);
}


int Xorriso_lst_destroy_all(struct Xorriso_lsT **lstring, int flag)
{
 struct Xorriso_lsT *s,*next;

 if(lstring==NULL)
   return(-1);
 if((*lstring)==NULL)
   return(0);
 for(s= *lstring; s->prev!=NULL; s= s->prev);
 for(;s!=NULL;s= next){
   next= s->next;
   Xorriso_lst_destroy(&s,0);
 }
 *lstring= NULL;
 return(1);
}


int Xorriso_lst_append_binary(struct Xorriso_lsT **entry,
                              char *data, int data_len, int flag)
{
 struct Xorriso_lsT *target= NULL,*newby;

 if(*entry!=NULL)
   for(target= *entry; target->next!=NULL; target= target->next);
 if(Xorriso_lst_new_binary(&newby, data, data_len, target, flag & ~1)<=0)
   return(-1);
 if(*entry==NULL || (flag & 1))
   *entry= newby;
 return(1);
}


struct Xorriso_lsT *Xorriso_lst_get_next(struct Xorriso_lsT *entry, int flag)
{
 return(entry->next);
}


struct Xorriso_lsT *Xorriso_lst_get_prev(struct Xorriso_lsT *entry, int flag)
{
 return(entry->prev);
}


char *Xorriso_lst_get_text(struct Xorriso_lsT *entry, int flag)
{
 return(entry->text);
}


int Xorriso_lst_detach_text(struct Xorriso_lsT *entry, int flag)
{
 entry->text= NULL;
 return(1);
}


int Xorriso_lst_get_last(struct Xorriso_lsT *entry, struct Xorriso_lsT **last,
                         int flag)
{
 *last= NULL;
 if(entry != NULL)
   for((*last)= entry; (*last)->next != NULL; (*last)= (*last)->next);
 return(1);
} 


/* --------------------------- End Xorriso_lsT ---------------------------- */


/* ------------------------------ ExclusionS ------------------------------ */


struct ExclusionS {

 /* Absolute input patterns which lead to not_paths */
 struct Xorriso_lsT *not_paths_descr;

 /* Actually banned absolute paths */
 struct Xorriso_lsT *not_paths;

 /* Input patterns which lead to not_leafs */
 struct Xorriso_lsT *not_leafs_descr;

 /* Compiled not_leaf patterns. Caution: not char[] but  regex_t */
 struct Xorriso_lsT *not_leafs;
 
};


int Exclusions_new(struct ExclusionS **o, int flag)
{
 struct ExclusionS *m;

 m= *o= TSOB_FELD(struct ExclusionS, 1);
 if(m==NULL)
   return(-1);
 m->not_paths_descr= NULL;
 m->not_paths= NULL;
 m->not_leafs_descr= NULL;
 m->not_leafs= NULL;
 return(1);
}


int Exclusions_destroy(struct ExclusionS **o, int flag)
{
 struct Xorriso_lsT *s,*next;

 if((*o)==NULL)
   return(0);
 Xorriso_lst_destroy_all(&((*o)->not_paths_descr), 0);
 Xorriso_lst_destroy_all(&((*o)->not_paths), 0);
 Xorriso_lst_destroy_all(&((*o)->not_leafs_descr), 0);
 for(s= (*o)->not_leafs; s!=NULL; s= next){
   next= s->next;
   regfree((regex_t *) s->text);
   Xorriso_lst_destroy(&s, 0);
 }
 free((char *) *o);
 (*o)= NULL;
 return(1);
}


int Exclusions_add_not_paths(struct ExclusionS *o, int descrc, char **descrs,
                             int pathc, char **paths, int flag)
{
 struct Xorriso_lsT *s, *new_s;
 int i, ret;

 s= NULL;
 if(o->not_paths_descr!=NULL) 
   for(s= o->not_paths_descr; s->next!=NULL; s= s->next);
 for(i= 0; i<descrc; i++) {
   ret= Xorriso_lst_new(&new_s, descrs[i], s, 0);
   if(ret<=0)
     return(ret);
   if(o->not_paths_descr==NULL)
     o->not_paths_descr= new_s;
   s= new_s;
 }
 s= NULL;
 if(o->not_paths!=NULL) 
   for(s= o->not_paths; s->next!=NULL; s= s->next);
 for(i= 0; i<pathc; i++) {
   ret= Xorriso_lst_new(&new_s, paths[i], s, 0);
   if(ret<=0)
     return(ret);
   if(o->not_paths==NULL)
     o->not_paths= new_s;
   s= new_s;
 }
 return(1);
}


/* @return -1=cannot store , 0=cannot compile regex , 1=ok
*/
int Exclusions_add_not_leafs(struct ExclusionS *o, char *not_leafs_descr,
                             regex_t *re, int flag)
{
 int ret;

 ret= Xorriso_lst_append_binary(&(o->not_leafs_descr),
                            not_leafs_descr, strlen(not_leafs_descr)+1, 0);
 if(ret<=0)
   return(-1);
 ret= Xorriso_lst_append_binary(&(o->not_leafs), (char *) re, sizeof(regex_t), 0);
 if(ret<=0)
   return(-1);
 return(1);
}


/* @param flag bit0= whole subtree is banned with -not_paths 
   @return 0=no match , 1=not_paths , 2=not_leafs, <0=error
*/
int Exclusions_match(struct ExclusionS *o, char *abs_path, int flag)
{
 struct Xorriso_lsT *s;
 char leaf[SfileadrL], *leaf_pt;
 regmatch_t match[1];
 int ret, was_non_slash, l;

 /* test abs_paths */
 if(flag&1) {
   for(s= o->not_paths; s!=NULL; s= s->next) {
     l= strlen(s->text);
     if(strncmp(abs_path, s->text, l)==0)
       if(abs_path[l]=='/' || abs_path[l]==0)
         return(1);
   }
 } else {
   for(s= o->not_paths; s!=NULL; s= s->next)
     if(strcmp(abs_path, s->text)==0)
       return(1);
 }

 /* determine leafname */
 was_non_slash= 0;
 for(leaf_pt= abs_path+strlen(abs_path); leaf_pt >= abs_path; leaf_pt--) {
   if(*leaf_pt=='/') {
     if(was_non_slash) {
       leaf_pt++;
 break;
     }
   } else if(*leaf_pt!=0)
     was_non_slash= 1;
 }
 if(strlen(leaf_pt)>=SfileadrL)
   return(-1);
 strcpy(leaf, leaf_pt);
 leaf_pt= strchr(leaf, '/');
 if(leaf_pt!=NULL)
   *leaf_pt= 0;

 /* test with leaf expressions */
 for(s= o->not_leafs; s!=NULL; s= s->next) {
   ret= regexec((regex_t *) s->text, leaf, 1, match, 0);
   if(ret==0)
     return(2);
 }
 return(0);
}


int Exclusions_get_descrs(struct ExclusionS *o,
                          struct Xorriso_lsT **not_paths_descr,
                          struct Xorriso_lsT **not_leafs_descr, int flag)
{
 *not_paths_descr= o->not_paths_descr;
 *not_leafs_descr= o->not_leafs_descr;
 return(1);
}

/* ---------------------------- End ExclusionS ---------------------------- */


/* ------------------------------ LinkiteM -------------------------------- */

struct LinkiteM {
 char *link_path;
 dev_t target_dev;
 ino_t target_ino;
 int link_count;
 struct LinkiteM *next;
};


int Linkitem_new(struct LinkiteM **o, char *link_path, dev_t target_dev,
                 ino_t target_ino, struct LinkiteM *next, int flag)
{
 struct LinkiteM *m;

 m= *o= TSOB_FELD(struct LinkiteM,1);
 if(m==NULL)
   return(-1);
 m->target_dev= target_dev;
 m->target_ino= target_ino;
 m->next= next;
 m->link_count= 1;
 if(next!=NULL)
   m->link_count= m->next->link_count+1;
 m->link_path= strdup(link_path);
 if(m->link_path==NULL)
   goto failed;
 return(1);
failed:;
 Linkitem_destroy(o, 0);
 return(-1);
}


int Linkitem_destroy(struct LinkiteM **o, int flag)
{
 if((*o)==NULL)
   return(0);
 if((*o)->link_path!=NULL)
   free((*o)->link_path);
 free((char *) (*o));
 *o= NULL;
 return(1);
}


int Linkitem_reset_stack(struct LinkiteM **o, struct LinkiteM *to, int flag)
{
 struct LinkiteM *m, *m_next= NULL;

 /* Prevent memory corruption */
 for(m= *o;  m!=to; m= m->next)
   if(m==NULL) { /* this may actually not happen */
     *o= to;
     return(-1);
   }

 for(m= *o; m!=to; m= m_next) {
   m_next= m->next;
   Linkitem_destroy(&m, 0);
 }
 *o= to;
 return(1);
}


int Linkitem_find(struct LinkiteM *stack, dev_t target_dev, ino_t target_ino,
                  struct LinkiteM **result, int flag)
{
 struct LinkiteM *m;

 for(m= stack; m!=NULL; m= m->next) {
   if(target_dev == m->target_dev && target_ino == m->target_ino) {
     *result= m;
     return(1);
   }
 }
 return(0);
}


int Linkitem_get_link_count(struct LinkiteM *item, int flag)
{
 return(item->link_count);
}


/* ------------------------------ PermstacK ------------------------------- */


struct PermiteM {
 char *disk_path;
 struct stat stbuf;
 struct PermiteM *next;
};


int Permstack_push(struct PermiteM **o, char *disk_path, struct stat *stbuf,
                   int flag)
{
 struct PermiteM *m;

 m= TSOB_FELD(struct PermiteM,1);
 if(m==NULL)
   return(-1);
 m->disk_path= NULL;
 memcpy(&(m->stbuf), stbuf, sizeof(struct stat));
 m->next= *o;

 m->disk_path= strdup(disk_path);
 if(m->disk_path==NULL)
   goto failed;

 *o= m;
 return(1);
failed:;
 if(m->disk_path!=NULL)
   free(m->disk_path);
 free((char *) m);
 return(-1);
}


/* @param flag bit0= minimal transfer: access permissions only
               bit1= do not set timestamps
*/ 
int Permstack_pop(struct PermiteM **o, struct PermiteM *stopper,
                  struct XorrisO *xorriso, int flag)
{
 int ret;
 char sfe[5*SfileadrL];
 struct utimbuf utime_buffer;
 struct PermiteM *m, *m_next;

 if((*o)==stopper)
   return(1);
 for(m= *o; m!=NULL; m= m->next)
   if(m->next==stopper)
 break;
 if(m==NULL) {
   sprintf(xorriso->info_text,
           "Program error: Permstack_pop() : cannot find stopper");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }

 for(m= *o; m!=stopper; m= m_next) {
   ret= chmod(m->disk_path, m->stbuf.st_mode);
   if(ret==-1) {
     if(xorriso!=NULL) {
       sprintf(xorriso->info_text,
             "Cannot change access permissions of disk directory: chmod %o %s",
             (unsigned int) (m->stbuf.st_mode & 07777),
             Text_shellsafe(m->disk_path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",
                           0);
     }
   }
   if(!(flag&1)) {
     ret= chown(m->disk_path, m->stbuf.st_uid, m->stbuf.st_gid);
                                               /* don't complain if it fails */
     if(!(flag&2)) {
       utime_buffer.actime= m->stbuf.st_atime;
       utime_buffer.modtime= m->stbuf.st_mtime;
       ret= utime(m->disk_path,&utime_buffer);
       if(ret==-1 && xorriso!=NULL) {
         sprintf(xorriso->info_text,
                 "Cannot change timestamps of disk directory: %s",
                 Text_shellsafe(m->disk_path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",
                             0);
       }
     }
   }
   m_next= m->next;
   free(m->disk_path);
   free((char *) m);
   *o= m_next;
 }
 return(1);
}


/* ---------------------------- End PermstacK ----------------------------- */

