X-Git-Url: http://git.jankratochvil.net/?p=udpgate.git;a=blobdiff_plain;f=src%2Fbundle-util.c;h=920049392a75e593cf64aff9ff570b26e5f77a7c;hp=30e1b77ea5fa7c1666856b376f0d602b4bead731;hb=a428d9fec54dd0fd1bbc5916b3b69619bb8fbfe5;hpb=6bb01f7e93b4d665f5dc27bebea7b15bda645539 diff --git a/src/bundle-util.c b/src/bundle-util.c index 30e1b77..9200493 100644 --- a/src/bundle-util.c +++ b/src/bundle-util.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "bundle-util.h" /* self */ #include "bundle.h" @@ -124,23 +125,132 @@ gboolean bundle_util_file_remove(const gchar *pathname,const gchar *basename) return TRUE; } -gboolean bundle_util_file_write(const gchar *pathname,const gchar *basename,mode_t pathname_mode,gboolean pathname_backup) +struct dir_stack { + struct dir_stack *next; + gchar *dirname; /* in fact 'pathname' */ + }; + +/* Always creates up to 'dirname(pathname)', never 'pathname' itself! */ +static struct dir_stack *pathname_mkdirs(const gchar *pathname,mode_t mode) +{ +gchar *dirname; +struct dir_stack *r; + + g_return_val_if_fail(pathname!=NULL,NULL); + + dirname=g_path_get_dirname(pathname); + if (!strcmp(dirname,pathname)) { + g_free(dirname); + dirname=NULL; + } + if (!dirname) + return NULL; + r=pathname_mkdirs(dirname,mode); + if (mkdir(dirname,mode)) + g_free(dirname); + else { +struct dir_stack *dir_stack; + + udpgate_new(dir_stack); + dir_stack->dirname=dirname; + dir_stack->next=r; + r=dir_stack; + } + return r; +} + +static struct dir_stack *bundle_util_file_write_atexit_dir_stack_head; +struct file_stack { + struct file_stack *next; + gchar *pathname; + gchar *basename; + }; +static struct file_stack *bundle_util_file_write_atexit_file_stack_head; + +static void bundle_util_file_write_atexit(void) +{ +struct file_stack *file_stack; +struct dir_stack *dir_stack; + + /* Always remove files first before their directories! */ + while ((file_stack=bundle_util_file_write_atexit_file_stack_head)) { + /* Errors already reported: */ + bundle_util_file_remove(file_stack->pathname,file_stack->basename); + g_free(file_stack->pathname); + g_free(file_stack->basename); + bundle_util_file_write_atexit_file_stack_head=file_stack->next; + g_free(file_stack); + } + + while ((dir_stack=bundle_util_file_write_atexit_dir_stack_head)) { + if (rmdir(dir_stack->dirname)) + g_warning(_("Error cleaning up created temporary directory: %s"),dir_stack->dirname); + g_free(dir_stack->dirname); + bundle_util_file_write_atexit_dir_stack_head=dir_stack->next; + g_free(dir_stack); + } +} + +gboolean bundle_util_file_write(const gchar *pathname,const gchar *basename,mode_t pathname_mode, + enum bundle_util_flags flags) { const guint8 *data; guint32 data_length; int fd; +struct dir_stack *mkdirs=NULL; +static gboolean atexited=FALSE; g_return_val_if_fail(pathname!=NULL,FALSE); g_return_val_if_fail(basename!=NULL,FALSE); + g_return_val_if_fail(flags&(BUNDLE_UTIL_BACKUP_MASK|BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK),FALSE); + /* Currently just unsupported: */ + g_return_val_if_fail((flags&(BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK))!=BUNDLE_UTIL_MKDIRS_MASK,FALSE); if (!(data=bundle_util_file_retrieve(&data_length,basename))) return FALSE; - if (pathname_backup && !bundle_util_file_backup_conditional(pathname,basename)) + if ((flags&BUNDLE_UTIL_BACKUP_MASK) && !bundle_util_file_backup_conditional(pathname,basename)) return FALSE; + + if (flags&BUNDLE_UTIL_MKDIRS_MASK) { +mode_t dir_mode=pathname_mode; + + dir_mode|=(pathname_mode&0444)>>2; /* mode|=('r'->'x') */ + mkdirs=pathname_mkdirs(pathname,dir_mode); + } + + if (!atexited) { + atexited=TRUE; + g_atexit(bundle_util_file_write_atexit); + } + + if (flags&BUNDLE_UTIL_TEMPORARY_MASK) { +struct dir_stack **mkdirs_tail_pointer; +struct file_stack *file_stack; + + /* Stack current 'mkdirs' in front of the current: bundle_util_file_write_atexit_dir_stack_head + * to remove them in the reverse order than created. + * Removal ordering of the current 'mkdirs' stack is preserved + * as it is already reversed. + */ + for (mkdirs_tail_pointer=&mkdirs;*mkdirs_tail_pointer;mkdirs_tail_pointer=&(*mkdirs_tail_pointer)->next); + *mkdirs_tail_pointer=bundle_util_file_write_atexit_dir_stack_head; + bundle_util_file_write_atexit_dir_stack_head=mkdirs; + mkdirs=NULL; + + /* Register also the file itself. */ + udpgate_new(file_stack); + file_stack->pathname=g_strdup(pathname); + file_stack->basename=g_strdup(basename); + file_stack->next=bundle_util_file_write_atexit_file_stack_head; + bundle_util_file_write_atexit_file_stack_head=file_stack; + } + /* Currently just unsupported: */ + g_assert(!mkdirs); if (-1==(fd=open(pathname,O_WRONLY|O_CREAT|O_TRUNC,pathname_mode))) { - g_warning(_("Error opening the file \"%s\" for rewrite: %m"),pathname); + if (errno!=EPERM) + g_warning(_("Error opening the file \"%s\" for rewrite: %m"),pathname); return FALSE; } if ((int)data_length!=write(fd,data,data_length)) {