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 void bundle_util_file_insert(const void *data,gsize data_length,const gchar *basename)
60 g_return_if_fail(data!=NULL);
61 g_return_if_fail(basename!=NULL);
64 g_message(_("Internally storing autogenerated: %s"),basename);
65 udpgate_newn(buffer,sizeof(guint32)+data_length);
66 *((guint32 *)buffer)=GUINT32_TO_BE(data_length);
67 g_assert(data_length==GUINT32_FROM_BE(*(guint32 *)buffer));
68 memcpy(buffer+sizeof(data_length),data,data_length);
69 /* FIXME: Missing duplicity check! */
70 /* FIXME: Missing g_free() on the element removal! */
71 g_hash_table_insert(bundle_hash_new(),g_strdup(basename),buffer);
74 /* Returns TRUE if it safe to overwrite/unlink the file.
76 static gboolean bundle_util_file_backup_conditional(const gchar *pathname,const gchar *basename)
83 char strftime_buffer[LINE_MAX];
84 const gchar *destination;
87 g_return_val_if_fail(pathname!=NULL,FALSE);
88 g_return_val_if_fail(basename!=NULL,FALSE);
90 if (!(data=bundle_util_file_retrieve(&data_length,basename)))
93 if (-1==(fd=open(pathname,O_RDONLY))) {
97 g_warning(_("Error checking file modifications of \"%s\": %m"),pathname);
102 /* WARNING: 'data_found' allocated in this block! */
103 data_found=g_malloc(data_length+1);
104 if (-1==(got=read(fd,data_found,data_length+1)))
105 g_warning(_("Error reading during the check of file modifications of \"%s\": %m"),pathname);
107 g_warning(_("Error closing the file \"%s\" during the check of its modifications: %m"),pathname);
108 /* memcmp(3) requires 'data_found'! */
109 if (got==(int)data_length && !memcmp(data_found,data,data_length)) {
115 time_current=time(NULL); /* It is segfault to gmtime(NULL). */
116 if (!strftime(strftime_buffer,sizeof(strftime_buffer),"GMT%FT%T",gmtime(&time_current))) {
117 g_warning("strftime(3): %m"); /* shouldn't happen */
120 destination=udpgate_printf_alloca("%s-%s-%d",pathname,strftime_buffer,(int)getpid());
121 if (rename(pathname,destination)) {
122 g_warning(_("Error renaming your modified file \"%s\" to the backup \"%s\", giving up: %m"),
123 pathname,destination);
130 gboolean bundle_util_file_remove(const gchar *pathname,const gchar *basename)
132 g_return_val_if_fail(pathname!=NULL,FALSE);
133 g_return_val_if_fail(basename!=NULL,FALSE);
135 if (!bundle_util_file_backup_conditional(pathname,basename))
138 if (unlink(pathname) && errno!=ENOENT) {
139 g_warning(_("Error removing the file \"%s\": %m"),pathname);
147 struct dir_stack *next;
148 gchar *dirname; /* in fact 'pathname' */
151 /* Always creates up to 'dirname(pathname)', never 'pathname' itself! */
152 static struct dir_stack *pathname_mkdirs(const gchar *pathname,mode_t mode)
157 g_return_val_if_fail(pathname!=NULL,NULL);
159 dirname=g_path_get_dirname(pathname);
160 if (!strcmp(dirname,pathname)) {
166 r=pathname_mkdirs(dirname,mode);
167 if (mkdir(dirname,mode))
170 struct dir_stack *dir_stack;
172 udpgate_new(dir_stack);
173 dir_stack->dirname=dirname;
180 static struct dir_stack *bundle_util_file_write_atexit_dir_stack_head;
182 struct file_stack *next;
186 static struct file_stack *bundle_util_file_write_atexit_file_stack_head;
188 static void bundle_util_file_write_atexit(void)
190 struct file_stack *file_stack;
191 struct dir_stack *dir_stack;
193 /* Always remove files first before their directories! */
194 while ((file_stack=bundle_util_file_write_atexit_file_stack_head)) {
195 /* Errors already reported: */
196 bundle_util_file_remove(file_stack->pathname,file_stack->basename);
197 g_free(file_stack->pathname);
198 g_free(file_stack->basename);
199 bundle_util_file_write_atexit_file_stack_head=file_stack->next;
203 while ((dir_stack=bundle_util_file_write_atexit_dir_stack_head)) {
204 if (rmdir(dir_stack->dirname))
205 g_warning(_("Error cleaning up created temporary directory: %s"),dir_stack->dirname);
206 g_free(dir_stack->dirname);
207 bundle_util_file_write_atexit_dir_stack_head=dir_stack->next;
212 gboolean bundle_util_file_write(const gchar *pathname,const gchar *basename,mode_t pathname_mode,
213 enum bundle_util_flags flags)
218 struct dir_stack *mkdirs=NULL;
219 static gboolean atexited=FALSE;
221 g_return_val_if_fail(pathname!=NULL,FALSE);
222 g_return_val_if_fail(basename!=NULL,FALSE);
223 g_return_val_if_fail(flags&(BUNDLE_UTIL_BACKUP_MASK|BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK),FALSE);
224 /* Currently just unsupported: */
225 g_return_val_if_fail((flags&(BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK))!=BUNDLE_UTIL_MKDIRS_MASK,FALSE);
227 if (!(data=bundle_util_file_retrieve(&data_length,basename)))
230 if ((flags&BUNDLE_UTIL_BACKUP_MASK) && !bundle_util_file_backup_conditional(pathname,basename))
233 if (flags&BUNDLE_UTIL_MKDIRS_MASK) {
234 mode_t dir_mode=pathname_mode;
236 dir_mode|=(pathname_mode&0444)>>2; /* mode|=('r'->'x') */
237 mkdirs=pathname_mkdirs(pathname,dir_mode);
242 g_atexit(bundle_util_file_write_atexit);
245 if (flags&BUNDLE_UTIL_TEMPORARY_MASK) {
246 struct dir_stack **mkdirs_tail_pointer;
248 /* Stack current 'mkdirs' in front of the current: bundle_util_file_write_atexit_dir_stack_head
249 * to remove them in the reverse order than created.
250 * Removal ordering of the current 'mkdirs' stack is preserved
251 * as it is already reversed.
253 for (mkdirs_tail_pointer=&mkdirs;*mkdirs_tail_pointer;mkdirs_tail_pointer=&(*mkdirs_tail_pointer)->next);
254 *mkdirs_tail_pointer=bundle_util_file_write_atexit_dir_stack_head;
255 bundle_util_file_write_atexit_dir_stack_head=mkdirs;
258 /* Currently just unsupported: */
261 if (-1==(fd=open(pathname,O_WRONLY|O_CREAT|O_TRUNC,pathname_mode))) {
262 if (errno!=EACCES && errno!=ENOENT)
263 g_warning(_("Error opening the file \"%s\" for rewrite: %m"),pathname);
266 /* Register the file only if it got already created.
267 * Do not atempt to remove files which are not ours!
269 if (flags&BUNDLE_UTIL_TEMPORARY_MASK) {
270 struct file_stack *file_stack;
272 /* Register also the file itself. */
273 udpgate_new(file_stack);
274 file_stack->pathname=g_strdup(pathname);
275 file_stack->basename=g_strdup(basename);
276 file_stack->next=bundle_util_file_write_atexit_file_stack_head;
277 bundle_util_file_write_atexit_file_stack_head=file_stack;
280 if ((int)data_length!=write(fd,data,data_length)) {
281 g_warning(_("Error writing the data of the file \"%s\" being overwritten: %m"),pathname);
282 close(fd); /* errors ignored */
286 g_warning(_("Error closing the file \"%s\" after its rewrite: %m"),pathname);