diff --git a/test/xorriso.1 b/test/xorriso.1 index fd5296f7..a8e64153 100644 --- a/test/xorriso.1 +++ b/test/xorriso.1 @@ -70,10 +70,10 @@ Command processing .br Unlike other filesystems, ISO 9660 is not intended for read-write operation but rather for being generated in a single sweep and being written to media as a -.B session . +.B session. .br -The data content of a session is called filesystem -.B image . +The data content of the session is called filesystem +.B image. .PP The written image in its session can then be mounted by the operating system for being used read-only. Linux is able to mount ISO images from block devices, @@ -86,8 +86,11 @@ which allows to add information to the CD and gives the mount programs of the operating systems the addresses of the entry points of each session. The mount programs recognize block devices which represent CD media and will by default mount the image in the last session. +.br This session usually contains an updated directory tree for the whole media which governs the data contents in all recorded sessions. +So in the view of the mount program all sessions of a particular media +together form a single filesystem image. .PP The multi-session model of the MMC standard applies to CD-R[W], to DVD-R, to certain states of DVD-RW, and to DVD+R. But it does not apply to overwriteable @@ -118,8 +121,8 @@ drive with the eventual ISO image and ends by command -commit which triggers writing. A -commit is done automatically when the program ends regularly. .PP After -commit a new session begins. A new input drive can only be chosen -as long as the loaded ISO image was not altered. Alteration can be revoked -by command -rollback. +as long as the loaded ISO image was not altered. +Pending alteration can be revoked by command -rollback. .PP Writing a session to the target is supposed to be very expensive in terms of time and of consumed space on appendable or write-once media. Therefore all @@ -133,6 +136,7 @@ useful to store intermediate states and to continue with image manipulations. .br Source of an existing ISO image can be any random access readable libburn drive: optical media with readable data, regular files, block devices. +.br RockRidge info must be present in existing ISO images and it will be generated by the program unconditionally. .PP @@ -262,12 +266,15 @@ Note that in the ISO image you are as powerful as the superuser. Access permissions of the existing files in the image do not apply to your write operations. They are intended to be in effect with the read-only mounted image. .PP -If the iso_rr_path of a newly inserted or renamed file leads to an existing +If the iso_rr_path of a newly inserted file leads to an existing file object in the ISO image, then the following collision handling happens: .br If both objects are directories then they get merged by recursively inserting the subobjects from filesystem into ISO image. If other file types collide then the setting of command -overwrite decides. +.br +Renaming of files has similar collision handling, but directories can only +be replaced, not merged. .PP The commands in this section alter the ISO image and not the local filesystem. .TP @@ -515,9 +522,15 @@ Describe terminal to the text pager. ? \fB\-use_readline\fR "on"|"off" If "on" then use readline for dialog. Else use plain stdin. .TP -> \fB\-reassure\fR "on"|"off" -If "on" then ask the user for "y" or "n" with any file -before deleting or overwriting it in the ISO image. +\fB\-reassure\fR "on"|"tree"|"off" +If "on" then ask the user for "y" or "n" +before deleting or overwriting any file in the ISO image. +With setting "tree" the reassuring prompt will appear for an eventual +directory only once and not for each file in its whole subtree. +Setting "off" silently kills any kind of image file object. +.br +Note that the prompt appears only in situations where file removal +is not forbidden by option -overwrite. .TP .B Drive and media related inquiry actions: .TP diff --git a/test/xorriso.c b/test/xorriso.c index e83408fd..6056778e 100644 --- a/test/xorriso.c +++ b/test/xorriso.c @@ -1815,6 +1815,7 @@ int Xorriso_new(struct XorrisO ** xorriso,char *progname, int flag) m->bar_is_fresh= 0; m->pending_option[0]= 0; m->request_to_abort= 0; + m->request_not_to_ask= 0; m->idle_time= 0.0; m->re_failed_at= -1; m->prepended_wd= 0; @@ -1962,11 +1963,16 @@ int Xorriso_request_confirmation(struct XorrisO *xorriso, int flag) bit0= important operation going on: demand confirmation of abort, only abort on @@@ bit1= mark '@' and '@@' by return 4 - bit2= accept: i=ignore , r=retry , x=abort + bit2= accept: i|n= ignore | do not remove , r|y= retry | remove , q|x= abort bit3= @@@ = 'done reading' rather than 'abort' */ -/* return: <=0 error , 1=go on , 2=abort , 3=redo request, 4=see flag bit1 - (5=skip volume) , 6=retry failed operation +/* return: <=0 error + 1= go on | do not remove existing file + 2= abort + 3= redo request for confirmation + 4= see flag bit1 + (5= skip volume) + 6= retry failed operation | remove existing file */ { int ret; @@ -2030,7 +2036,7 @@ klammer_affe:; xorriso->result_page_length= -xorriso->result_page_length; if(flag&1) { sprintf(xorriso->info_text, -"... [@ = page prompt suppression registered. -page disabled temporarily ] ...\n"); +"... [@ = prompt suppression registered. Prompting disabled temporarily ] ...\n"); Xorriso_info(xorriso,0); } @@ -2048,16 +2054,20 @@ klammer_affe:; return(1); } else if(flag&4) { - if(strcmp(cpt,"i")==0 || strcmp(cpt,"I")==0 || *cpt==0) { + if(strcmp(cpt,"i")==0 || strcmp(cpt,"I")==0 || + strcmp(cpt,"n")==0 || strcmp(cpt,"N")==0 || + *cpt==0) { return(1); - } else if(strcmp(cpt,"r")==0 || strcmp(cpt,"R")==0) { + } else if(strcmp(cpt,"r")==0 || strcmp(cpt,"R")==0 || + strcmp(cpt,"y")==0 || strcmp(cpt,"Y")==0) { return(6); - } else if(strcmp(cpt,"x")==0 || strcmp(cpt,"X")==0) { + } else if(strcmp(cpt,"x")==0 || strcmp(cpt,"X")==0 || + strcmp(cpt,"q")==0 || strcmp(cpt,"Q")==0) { goto request_to_abort; } else { /* >>> unknown input */ sprintf(xorriso->info_text, - "--- Please enter one of : empty line, i, r, x, @, @@, @@@\n"); + "--- Please enter one of : empty line, i,n, r,y, q,x, @, @@, @@@\n"); Xorriso_info(xorriso,0); return(3); } @@ -2124,7 +2134,7 @@ int Xorriso_pager(struct XorrisO *xorriso, char *line, int flag) int ret,linecount; char info_text[10*SfileadrL]; - if(xorriso->result_page_length<=0) + if(xorriso->result_page_length<=0 || xorriso->request_not_to_ask) return(1); Xorriso_predict_linecount(xorriso,line,&linecount,0); if(xorriso->result_line_counter+linecount>xorriso->result_page_length) { @@ -2180,6 +2190,7 @@ int Xorriso_info(struct XorrisO *xorriso, int flag) /* bit0= use pager (as with result) bit1= permission to suppress output + bit2= insist in showing output */ { int ret; @@ -2192,7 +2203,7 @@ int Xorriso_info(struct XorrisO *xorriso, int flag) if(note_sev==0) Xorriso__text_to_sev("NOTE", ¬e_sev, 0); if(note_sevreport_about_severity && - note_sevabort_on_severity) + note_sevabort_on_severity && !(flag&4)) return(1); if(flag&1) { @@ -2825,7 +2836,8 @@ int Xorriso_status(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= !xorriso->do_reassure; - sprintf(line,"-reassure %s\n",(xorriso->do_reassure ? "on" : "off")); + sprintf(line,"-reassure %s\n",(xorriso->do_reassure == 1 ? "on" : + (xorriso->do_reassure == 2 ? "tree" : "off"))); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); @@ -4553,10 +4565,20 @@ int Xorriso_option_pwdx(struct XorrisO *xorriso, int flag) } -/* Option -reassure "on"|"off" */ +/* Option -reassure "on"|"tree"|"off" */ int Xorriso_option_reassure(struct XorrisO *xorriso, char *mode, int flag) { - xorriso->do_reassure= !!strcmp(mode, "off"); + if(strcmp(mode, "off")==0) + xorriso->do_reassure= 0; + else if(strcmp(mode, "on")==0) + xorriso->do_reassure= 1; + else if(strcmp(mode, "tree")==0) + xorriso->do_reassure= 2; + else { + sprintf(xorriso->info_text, "-reassure: unknown mode '%s'", mode); + Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); + return(0); + } return(1); } @@ -4606,12 +4628,14 @@ int Xorriso_option_rmi(struct XorrisO *xorriso, int argc, char **argv, return(ret); strcpy(path, eff_path); - ret= Xorriso_rmi(xorriso, path, flag&1); + ret= Xorriso_rmi(xorriso, NULL, path, flag&1); if(ret<=0) goto ex; - sprintf(xorriso->info_text, "Removed from ISO image: %s '%s'\n", - (ret>1 ? "subtree" : "file"), path); - Xorriso_info(xorriso, 0); + if(ret<3) { + sprintf(xorriso->info_text, "Removed from ISO image: %s '%s'\n", + (ret>1 ? "subtree" : "file"), path); + Xorriso_info(xorriso, 0); + } } ret= 1; ex:; @@ -4754,7 +4778,7 @@ int Xorriso_option_tell_media_space(struct XorrisO *xorriso, int flag) "Pending image size larger than free space on media"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } - sprintf(xorriso->result_line, "Write space : %d\n", media_space); + sprintf(xorriso->result_line, "Media space : %d\n", media_space); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Free space : %d\n", free_space); Xorriso_result(xorriso, 0); @@ -4883,7 +4907,7 @@ return: next_command:; xorriso->prepended_wd= 0; - xorriso->request_to_abort= 0; + xorriso->request_to_abort= xorriso->request_not_to_ask= 0; Xorriso_set_problem_status(xorriso, "", 0); if((*idx)do_overwrite==1 || (xorriso->do_overwrite==2 && !target_is_dir)) { - ret= Xorriso_rmi(xorriso, img_path, 1); + ret= Xorriso_rmi(xorriso, NULL, img_path, 1|8); if(ret<=0) goto was_problem; + 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); + goto was_problem; + } node= NULL; } else { sprintf(xorriso->info_text, @@ -715,7 +725,7 @@ int Xorriso_graft_in(struct XorrisO *xorriso, char *disk_path, char *img_path, int flag) { struct iso_volume *volume; - char path[SfileadrL], *apt, *npt, *cpt; + char path[SfileadrL], *apt, *npt, *cpt, sfe[4*SfileadrL]; struct iso_tree_node_dir *dir; struct iso_tree_node *node; int done= 0, is_dir= 0, l, ret, target_is_dir, source_is_dir; @@ -810,9 +820,15 @@ int Xorriso_graft_in(struct XorrisO *xorriso, char *disk_path, char *img_path, /* handle overwrite situation */; if(xorriso->do_overwrite==1 || (xorriso->do_overwrite==2 && !target_is_dir)) { - ret= Xorriso_rmi(xorriso, path, 1); + ret= Xorriso_rmi(xorriso, NULL, path, 1|8); 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(0); + } node= NULL; goto handle_path_node; } @@ -1366,50 +1382,103 @@ int Xorriso_node_from_path(struct XorrisO *xorriso, struct iso_volume *volume, } -/* @param flag bit0= remove whole sub tree: rm -r +/* @param boss_iter If not NULL then this is an iterator suitable for + iso_tree_node_remove_iter() which is then to be used instead + of iso_tree_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 @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, char *path, int flag) +int Xorriso_rmi(struct XorrisO *xorriso, void *boss_iter, + char *path, int flag) { - int ret, is_dir= 0; - struct iso_tree_node *victim_node; - struct iso_tree_node_dir *boss_node; + int ret, is_dir= 0, pl, not_removed= 0; + struct iso_tree_node *victim_node, *node; + struct iso_tree_node_dir *boss_node, *root_dir; + struct iso_tree_iter *iter= NULL; struct iso_volume *volume; - char sfe[4*SfileadrL]; + char sfe[4*SfileadrL], sub_path[2*SfileadrL], *sub_name, *name; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) - return(ret); + goto ex; if(Xorriso_much_too_long(xorriso, strlen(path), 0)<=0) - return(0); + {ret= 0; goto ex;} ret= Xorriso_node_from_path(xorriso, volume, path, &victim_node, 0); if(ret<=0) - return(ret); + goto ex; + root_dir= iso_volume_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, "SORRY", 0); + return(0); + } if(LIBISO_ISDIR(victim_node)) is_dir= 1; - if(!(flag&1)) { /* not rm -r */ - if(is_dir) { + if(is_dir) { + if(flag&1) { /* rm -r */ + if(xorriso->do_reassure==1 && !xorriso->request_not_to_ask) { + /* Iterate over subordinates and delete them */ + /* this might become standard if access permissions get respected */ + + iter= iso_tree_node_children((struct iso_tree_node_dir *) victim_node); + Xorriso_process_msg_queues(xorriso,0); + 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((node= iso_tree_iter_next(iter)) != NULL + && !xorriso->request_to_abort) { + name= (char *) iso_tree_node_get_name(node); + if(Xorriso_much_too_long(xorriso, pl+1+strlen(name), 0)<=0) + {ret= 0; goto ex;} + strcpy(sub_name, name); + ret= Xorriso_rmi(xorriso, iter, sub_path, (flag&(1|2|8))|4); + if(ret<=0) + goto ex; + if(ret==3) + not_removed= 1; + } + if(not_removed) { + if(!(flag&4)) { + sprintf(xorriso->info_text, "Directory not removed: %s", + Text_shellsafe(path, sfe, 0)); + Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 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, "SORRY", 0); - return(0); + ret= 0; goto ex; } - /* >>> check whether directory is empty */; - - sprintf(xorriso->info_text, - "Single directory removal not implemented yet"); - Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); - return(0); + iter= iso_tree_node_children((struct iso_tree_node_dir *) victim_node); + Xorriso_process_msg_queues(xorriso,0); + if(iso_tree_iter_next(iter)!=NULL) { + sprintf(xorriso->info_text, "Directory not empty on attempt to delete"); + Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); + ret= 0; goto ex; + } } } + if(xorriso->request_to_abort) + {ret= 3; goto ex;} + boss_node= iso_tree_node_get_parent(victim_node); Xorriso_process_msg_queues(xorriso,0); if(boss_node==NULL) { @@ -1417,26 +1486,69 @@ int Xorriso_rmi(struct XorrisO *xorriso, char *path, int flag) "Cannot find parent node of %s in loaded ISO image", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); - return(0); + ret= 0; goto ex; } - if(xorriso->do_reassure) { - - /* >>> ask user */; - + while((xorriso->do_reassure==1 || (xorriso->do_reassure==2 && !(flag&4))) + && !xorriso->request_not_to_ask) { + Xorriso_ls_filev(xorriso, 1, &path, (off_t) 0, 1|2); /* ls -l */ + if(is_dir) + Xorriso_ls_filev(xorriso, 1, &path, (off_t) 0, 2|4); /* du -s */ + 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); + if(ret<=0) + goto ex; + if(xorriso->request_to_abort) { + if(!(flag&4)) { + 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; + } } - ret= iso_tree_node_remove(boss_node, victim_node); + if(boss_iter!=NULL) { + ret= iso_tree_node_remove_iter((struct iso_tree_iter *) boss_iter); + if(ret<0) + ret= -1; + } else + ret= iso_tree_node_remove(boss_node, victim_node); Xorriso_process_msg_queues(xorriso,0); if(ret==-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); - return(-1); + ret= -1; goto ex; } xorriso->volset_change_pending= 1; - return(1+!!is_dir); + ret= 1+!!is_dir; +ex:; + if(iter!=NULL) + iso_tree_iter_free(iter); + return(ret); } @@ -1620,6 +1732,7 @@ ex:; /* @param flag bit0= long format + bit1= do not print count of nodes bit2= du format */ int Xorriso_ls_filev(struct XorrisO *xorriso, int filec, char **filev, @@ -1671,7 +1784,7 @@ much_too_long:; } } - if(!(flag&4)) { + if(!(flag&(2|4))) { sprintf(xorriso->info_text, "Valid ISO nodes found: %d\n", filec-was_error); Xorriso_info(xorriso,0); if(filec-was_error<=0) @@ -1958,9 +2071,15 @@ int Xorriso_rename(struct XorrisO *xorriso, char *origin, char *dest, int flag) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } else if(dest_ret>0) { - ret= Xorriso_rmi(xorriso, eff_dest, 0); + ret= Xorriso_rmi(xorriso, NULL, eff_dest, 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 */ diff --git a/test/xorrisoburn.h b/test/xorrisoburn.h index 8ac5fc65..26b05f35 100644 --- a/test/xorrisoburn.h +++ b/test/xorrisoburn.h @@ -15,7 +15,6 @@ #define Xorrisoburn_includeD yes struct XorrisO; -struct burn_drive; int Xorriso_startup_libraries(struct XorrisO *xorriso, int flag); @@ -25,9 +24,6 @@ int Xorriso_give_up_drive(struct XorrisO *xorriso, int flag); int Xorriso_write_growing(struct XorrisO *xorriso, int flag); -int Xorriso_pacifier_loop(struct XorrisO *xorriso, struct burn_drive *drive, - int flag); - /* @param flag bit0=graft in as empty directory, not as copy from disk @return <=0 = error , 1 = added simple node , 2 = added directory */ @@ -49,13 +45,21 @@ int Xorriso_blank_media(struct XorrisO *xorriso, int flag); int Xorriso_format_media(struct XorrisO *xorriso, int flag); -/* @param flag bit0= remove whole sub tree: rm -r - bit1=remove empty directory: rmdir +/* @param boss_iter Opaque internal handle. Use NULL outside xorrisoburn.c : + If not NULL then this is an iterator suitable for + iso_tree_node_remove_iter() which is then to be used instead + of iso_tree_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 @return <=0 = error 1 = removed simple node - 2 = removed directory or subtree + 2 = removed directory or tree + 3 = did not remove on user revocation */ -int Xorriso_rmi(struct XorrisO *xorriso, char *path, int flag); +int Xorriso_rmi(struct XorrisO *xorriso, void *boss_iter, + char *path, int flag); /* @param flag bit0= long format */ int Xorriso_ls_filev(struct XorrisO *xorriso, int filec, char **filev,