2 * UDP Gateway single-file bundle utility functions
3 * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <glib/gmessages.h>
24 #include <sys/types.h>
27 #include <glib/gmem.h>
32 #include <glib/gstrfuncs.h>
34 #include "bundle-util.h" /* self */
39 static G_CONST_RETURN guint8 *bundle_util_file_retrieve(guint32 *data_length_return,const gchar *basename)
43 g_return_val_if_fail(data_length_return!=NULL,NULL);
44 g_return_val_if_fail(basename!=NULL,NULL);
47 g_message(_("Retrieving internally stored: %s"),basename);
48 data=g_hash_table_lookup(bundle_hash_new(),basename);
49 g_return_val_if_fail(data!=NULL,NULL);
50 *data_length_return=GUINT32_FROM_BE(*(guint32 *)data);
51 data+=sizeof(*data_length_return);
56 /* Returns TRUE if it safe to overwrite/unlink the file.
58 static gboolean bundle_util_file_backup_conditional(const gchar *pathname,const gchar *basename)
65 char strftime_buffer[LINE_MAX];
66 const gchar *destination;
69 g_return_val_if_fail(pathname!=NULL,FALSE);
70 g_return_val_if_fail(basename!=NULL,FALSE);
72 if (!(data=bundle_util_file_retrieve(&data_length,basename)))
75 if (-1==(fd=open(pathname,O_RDONLY))) {
79 g_warning(_("Error checking file modifications of \"%s\": %m"),pathname);
84 /* WARNING: 'data_found' allocated in this block! */
85 data_found=g_malloc(data_length+1);
86 if (-1==(got=read(fd,data_found,data_length+1)))
87 g_warning(_("Error reading during the check of file modifications of \"%s\": %m"),pathname);
89 g_warning(_("Error closing the file \"%s\" during the check of its modifications: %m"),pathname);
90 /* memcmp(3) requires 'data_found'! */
91 if (got==(int)data_length && !memcmp(data_found,data,data_length)) {
97 time_current=time(NULL); /* It is segfault to gmtime(NULL). */
98 if (!strftime(strftime_buffer,sizeof(strftime_buffer),"GMT%FT%T",gmtime(&time_current))) {
99 g_warning("strftime(3): %m"); /* shouldn't happen */
102 destination=udpgate_printf_alloca("%s-%s-%d",pathname,strftime_buffer,(int)getpid());
103 if (rename(pathname,destination)) {
104 g_warning(_("Error renaming your modified file \"%s\" to the backup \"%s\", giving up: %m"),
105 pathname,destination);
112 gboolean bundle_util_file_remove(const gchar *pathname,const gchar *basename)
114 g_return_val_if_fail(pathname!=NULL,FALSE);
115 g_return_val_if_fail(basename!=NULL,FALSE);
117 if (!bundle_util_file_backup_conditional(pathname,basename))
120 if (unlink(pathname) && errno!=ENOENT) {
121 g_warning(_("Error removing the file \"%s\": %m"),pathname);
129 struct dir_stack *next;
130 gchar *dirname; /* in fact 'pathname' */
133 /* Always creates up to 'dirname(pathname)', never 'pathname' itself! */
134 static struct dir_stack *pathname_mkdirs(const gchar *pathname,mode_t mode)
139 g_return_val_if_fail(pathname!=NULL,NULL);
141 dirname=g_path_get_dirname(pathname);
142 if (!strcmp(dirname,pathname)) {
148 r=pathname_mkdirs(dirname,mode);
149 if (mkdir(dirname,mode))
152 struct dir_stack *dir_stack;
154 udpgate_new(dir_stack);
155 dir_stack->dirname=dirname;
162 static struct dir_stack *bundle_util_file_write_atexit_dir_stack_head;
164 struct file_stack *next;
168 static struct file_stack *bundle_util_file_write_atexit_file_stack_head;
170 static void bundle_util_file_write_atexit(void)
172 struct file_stack *file_stack;
173 struct dir_stack *dir_stack;
175 /* Always remove files first before their directories! */
176 while ((file_stack=bundle_util_file_write_atexit_file_stack_head)) {
177 /* Errors already reported: */
178 bundle_util_file_remove(file_stack->pathname,file_stack->basename);
179 g_free(file_stack->pathname);
180 g_free(file_stack->basename);
181 bundle_util_file_write_atexit_file_stack_head=file_stack->next;
185 while ((dir_stack=bundle_util_file_write_atexit_dir_stack_head)) {
186 if (rmdir(dir_stack->dirname))
187 g_warning(_("Error cleaning up created temporary directory: %s"),dir_stack->dirname);
188 g_free(dir_stack->dirname);
189 bundle_util_file_write_atexit_dir_stack_head=dir_stack->next;
194 gboolean bundle_util_file_write(const gchar *pathname,const gchar *basename,mode_t pathname_mode,
195 enum bundle_util_flags flags)
200 struct dir_stack *mkdirs=NULL;
201 static gboolean atexited=FALSE;
203 g_return_val_if_fail(pathname!=NULL,FALSE);
204 g_return_val_if_fail(basename!=NULL,FALSE);
205 g_return_val_if_fail(flags&(BUNDLE_UTIL_BACKUP_MASK|BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK),FALSE);
206 /* Currently just unsupported: */
207 g_return_val_if_fail((flags&(BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK))!=BUNDLE_UTIL_MKDIRS_MASK,FALSE);
209 if (!(data=bundle_util_file_retrieve(&data_length,basename)))
212 if ((flags&BUNDLE_UTIL_BACKUP_MASK) && !bundle_util_file_backup_conditional(pathname,basename))
215 if (flags&BUNDLE_UTIL_MKDIRS_MASK) {
216 mode_t dir_mode=pathname_mode;
218 dir_mode|=(pathname_mode&0444)>>2; /* mode|=('r'->'x') */
219 mkdirs=pathname_mkdirs(pathname,dir_mode);
224 g_atexit(bundle_util_file_write_atexit);
227 if (flags&BUNDLE_UTIL_TEMPORARY_MASK) {
228 struct dir_stack **mkdirs_tail_pointer;
229 struct file_stack *file_stack;
231 /* Stack current 'mkdirs' in front of the current: bundle_util_file_write_atexit_dir_stack_head
232 * to remove them in the reverse order than created.
233 * Removal ordering of the current 'mkdirs' stack is preserved
234 * as it is already reversed.
236 for (mkdirs_tail_pointer=&mkdirs;*mkdirs_tail_pointer;mkdirs_tail_pointer=&(*mkdirs_tail_pointer)->next);
237 *mkdirs_tail_pointer=bundle_util_file_write_atexit_dir_stack_head;
238 bundle_util_file_write_atexit_dir_stack_head=mkdirs;
241 /* Register also the file itself. */
242 udpgate_new(file_stack);
243 file_stack->pathname=g_strdup(pathname);
244 file_stack->basename=g_strdup(basename);
245 file_stack->next=bundle_util_file_write_atexit_file_stack_head;
246 bundle_util_file_write_atexit_file_stack_head=file_stack;
248 /* Currently just unsupported: */
251 if (-1==(fd=open(pathname,O_WRONLY|O_CREAT|O_TRUNC,pathname_mode))) {
253 g_warning(_("Error opening the file \"%s\" for rewrite: %m"),pathname);
256 if ((int)data_length!=write(fd,data,data_length)) {
257 g_warning(_("Error writing the data of the file \"%s\" being overwritten: %m"),pathname);
258 close(fd); /* errors ignored */
262 g_warning(_("Error closing the file \"%s\" after its rewrite: %m"),pathname);