"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-output-stdio.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-output-stdio.c: stdio based output
4  *
5  * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-output-stdio.h>
24 #include <gsf/gsf-output-impl.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-utils.h>
27 #include <glib/gstdio.h>
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_SYS_STATFS_H
38 #include <sys/statfs.h>
39 #endif
40
41 #ifdef G_OS_WIN32
42 #include <wchar.h>
43 #include <direct.h>
44 #include <glib.h>
45 #ifdef HAVE_IO_H
46 #include <io.h>
47 #endif
48 #endif /* G_OS_WIN32 */
49
50 #ifndef W_OK
51 #define W_OK 2
52 #endif
53
54 static GObjectClass *parent_class;
55
56 struct _GsfOutputStdio {
57         GsfOutput output;
58
59         FILE     *file;
60         char     *real_filename, *temp_filename;
61         gboolean  create_backup_copy, keep_open;
62         struct stat st;
63 };
64
65 typedef struct {
66         GsfOutputClass output_class;
67 } GsfOutputStdioClass;
68
69 static int
70 rename_wrapper (char const *oldfilename, char const *newfilename)
71 {
72         int result = g_rename (oldfilename, newfilename);
73         if (!result)
74                 goto done;
75
76 #ifdef HAVE_SYS_STATFS_H
77 #ifdef HAVE_2ARG_STATFS
78         /* The FUSE file system does not unlink the target.  */
79         if (errno == EPERM) {
80                 int save_errno = errno;
81                 struct statfs buf;
82                 if (statfs (newfilename, &buf) == 0 &&
83                     memcmp (&buf.f_type, "FUse", 4) == 0)
84                         goto unlink_and_retry;
85                 errno = save_errno;
86         }
87 #endif
88 #endif
89
90 #ifdef G_OS_WIN32
91         /* Win32's rename does not unlink the target.  */
92         goto unlink_and_retry;
93 #endif
94
95 done:
96         return result;
97
98 unlink_and_retry:
99         (void)g_unlink (newfilename);
100         result = g_rename (oldfilename, newfilename);
101         goto done;
102 }
103
104 static int
105 chmod_wrapper (const char *filename, mode_t mode)
106 {
107 #ifdef HAVE_G_CHMOD
108         return g_chmod (filename, mode);
109 #else
110         return chmod (filename, mode);
111 #endif
112 }
113
114 static int
115 access_wrapper (char const *filename, int what)
116 {
117 #ifdef HAVE_G_ACCESS
118         return g_access (filename, what);
119 #else
120         return access (filename, what);
121 #endif
122 }
123
124 #define GSF_MAX_LINK_LEVEL 256
125
126 /* Calls g_file_read_link() until we find a real filename. */
127 static char *
128 follow_symlinks (char const *filename, GError **error)
129 {
130         gchar *followed_filename, *link;
131         gint link_count = 0;
132
133         g_return_val_if_fail (filename != NULL, NULL);
134
135         followed_filename = g_strdup (filename);
136
137         while ((link = g_file_read_link (followed_filename, NULL)) != NULL &&
138           ++link_count <= GSF_MAX_LINK_LEVEL) {
139                 if (g_path_is_absolute (link)) {
140                         g_free (followed_filename);
141                         followed_filename = link;
142                 } else {
143                         /* If the linkname is not an absolute path name, append
144                          * it to the directory name of the followed filename.  E.g.
145                          * we may have /foo/bar/baz.lnk -> eek.txt, which really
146                          * is /foo/bar/eek.txt.  */
147                         gchar *dir = g_path_get_dirname (followed_filename);
148                         g_free (followed_filename);
149                         followed_filename = g_build_filename (dir, link, NULL);
150                         g_free (dir);
151                         g_free (link);
152                 }
153         }
154
155         if (link == NULL)
156                 return followed_filename;
157
158         /* Too many symlinks */
159         if (error != NULL) {
160 #ifdef ELOOP
161                 int err = ELOOP;
162 #else
163                 /* We have links, but not ELOOP.  Very strange.  */
164                 int err = EINVAL;
165 #endif
166                 *error = g_error_new_literal (gsf_output_error_id (), err,
167                                               g_strerror (err));
168         }
169         g_free (followed_filename);
170         return NULL;
171 }
172
173 static gboolean
174 close_file_helper (GsfOutputStdio *stdio, gboolean seterr)
175 {
176         gboolean res = (0 == fclose (stdio->file));
177         stdio->file = NULL;
178         if (!res && seterr)
179                 gsf_output_set_error (GSF_OUTPUT (stdio), errno,
180                                       "Failed to close file: %s",
181                                       g_strerror (errno));
182         return res;
183 }
184
185 static gboolean
186 unlink_file_helper (GsfOutputStdio *stdio)
187 {
188         if (!stdio->temp_filename)
189                 return TRUE;
190
191         if (g_unlink (stdio->temp_filename) == 0) {
192                 g_free (stdio->temp_filename);
193                 stdio->temp_filename = NULL;
194                 return TRUE;
195         }
196
197         return FALSE;
198 }
199
200 static gboolean
201 gsf_output_stdio_close (GsfOutput *output)
202 {
203         GsfOutputStdio *stdio = GSF_OUTPUT_STDIO (output);
204         gboolean res;
205         char *backup_filename = NULL;
206
207         if (stdio->file == NULL)
208                 return FALSE;
209
210         if (gsf_output_error (output)) {
211                 res = TRUE;
212                 if (!stdio->keep_open && !close_file_helper (stdio, FALSE))
213                         res = FALSE;
214
215                 if (!unlink_file_helper (stdio))
216                         res = FALSE;
217
218                 return res;
219         }
220
221         if (stdio->keep_open) {
222                 gboolean res = (0 == fflush (stdio->file));
223                 if (!res)
224                         gsf_output_set_error (output, errno,
225                                               "Failed to flush.");
226                 stdio->file = NULL;
227                 return res;
228         }
229
230         res = close_file_helper (stdio, TRUE);
231
232         /* short circuit our when dealing with raw FILE */
233         if (!stdio->real_filename)
234                 return res;
235         if (!res) {
236                 unlink_file_helper (stdio);
237                 return FALSE;
238         }
239
240         /* Move the original file to a backup */
241         if (stdio->create_backup_copy) {
242                 gint result;
243                 backup_filename = g_strconcat (stdio->real_filename, ".bak", NULL);
244                 result = rename_wrapper (stdio->real_filename, backup_filename);
245                 if (result != 0) {
246                         char *utf8name = g_filename_display_name (backup_filename);
247                         gsf_output_set_error (output, errno,
248                                               "Could not backup the original as %s.",
249                                               utf8name);
250                         g_free (utf8name);
251                         g_free (backup_filename);
252                         g_unlink (stdio->temp_filename);
253                         return FALSE;
254                 }
255         }
256
257         /* Move the temp file to the original file */
258         if (rename_wrapper (stdio->temp_filename, stdio->real_filename) != 0) {
259                 gint saved_errno = errno;
260                 if (backup_filename != NULL &&
261                     rename_wrapper (backup_filename, stdio->real_filename) != 0)
262                         saved_errno = errno;
263                 res = gsf_output_set_error (output,
264                                             saved_errno,
265                                             "%s", g_strerror (saved_errno));
266         } else {
267                 /* Restore permissions.  There is not much error checking we
268                  * can do here, I'm afraid.  The final data is saved anyways.
269                  * Note the order: mode, uid+gid, gid, uid, mode.
270                  */
271                 chmod_wrapper (stdio->real_filename, stdio->st.st_mode);
272 #ifdef HAVE_CHOWN
273                 if (chown (stdio->real_filename,
274                            stdio->st.st_uid,
275                            stdio->st.st_gid)) {
276                         /* We cannot set both.  Maybe we can set one.  */
277                         chown (stdio->real_filename, -1, stdio->st.st_gid);
278                         chown (stdio->real_filename, stdio->st.st_uid, -1);
279                 }
280                 chmod_wrapper (stdio->real_filename, stdio->st.st_mode);
281 #endif
282         }
283
284         g_free (backup_filename);
285
286         return res;
287 }
288
289 static void
290 gsf_output_stdio_finalize (GObject *obj)
291 {
292         GsfOutput       *output = (GsfOutput *)obj;
293         GsfOutputStdio  *stdio = GSF_OUTPUT_STDIO (output);
294
295         if (!gsf_output_is_closed (output))
296                 gsf_output_close (output);
297
298         g_free (stdio->real_filename);
299         stdio->real_filename = NULL;
300         g_free (stdio->temp_filename);
301         stdio->temp_filename = NULL;
302
303         parent_class->finalize (obj);
304 }
305
306 static gboolean
307 gsf_output_stdio_seek (GsfOutput *output, gsf_off_t offset, GSeekType whence)
308 {
309         GsfOutputStdio const *stdio = GSF_OUTPUT_STDIO (output);
310         int stdio_whence = 0;   /* make compiler shut up */
311
312 #ifndef HAVE_FSEEKO
313         long loffset;
314 #else
315         off_t loffset;
316 #endif
317
318         g_return_val_if_fail (stdio->file != NULL, 
319                               gsf_output_set_error (output, 0, "missing file"));
320
321         loffset = offset;
322         if ((gsf_off_t) loffset != offset) { /* Check for overflow */
323 #ifdef HAVE_FSEEKO
324                 g_warning ("offset too large for fseeko");
325                 return gsf_output_set_error (output, 0, "offset too large for fseeko");
326 #else
327                 g_warning ("offset too large for fseek");
328                 return gsf_output_set_error (output, 0, "offset too large for fseek");
329 #endif
330         }
331         switch (whence) {
332         default : ; /*checked in GsfOutput wrapper */
333         case G_SEEK_SET : stdio_whence = SEEK_SET;      break;
334         case G_SEEK_CUR : stdio_whence = SEEK_CUR;      break;
335         case G_SEEK_END : stdio_whence = SEEK_END;      break;
336         }
337
338         errno = 0;
339 #ifdef HAVE_FSEEKO
340         if (0 == fseeko (stdio->file, loffset, stdio_whence))
341                 return TRUE;
342 #else
343         if (0 == fseek (stdio->file, loffset, stdio_whence))
344                 return TRUE;
345 #endif
346         return gsf_output_set_error (output, errno, "%s", g_strerror (errno));
347 }
348
349 static gboolean
350 gsf_output_stdio_write (GsfOutput *output,
351                         size_t num_bytes,
352                         guint8 const *buffer)
353 {
354         GsfOutputStdio *stdio = GSF_OUTPUT_STDIO (output);
355         size_t written, remaining;
356
357         g_return_val_if_fail (stdio != NULL, FALSE);
358         g_return_val_if_fail (stdio->file != NULL, FALSE);
359
360         remaining = num_bytes;
361
362         while (remaining > 0) {
363                 written = fwrite (buffer + (num_bytes - remaining), 1, 
364                                   remaining, stdio->file);
365                 if ((written < remaining) && ferror (stdio->file) != 0)
366                         return gsf_output_set_error (output, errno, "%s", g_strerror (errno));
367
368                 remaining -= written;
369         }
370         return TRUE;
371 }
372
373 static gsf_off_t gsf_output_stdio_vprintf (GsfOutput *output,
374         char const *fmt, va_list args) G_GNUC_PRINTF (2, 0);
375
376 static gsf_off_t
377 gsf_output_stdio_vprintf (GsfOutput *output, char const *fmt, va_list args)
378 {
379         return vfprintf (((GsfOutputStdio *)output)->file, fmt, args);
380 }
381
382 static void
383 gsf_output_stdio_init (GObject *obj)
384 {
385         GsfOutputStdio *stdio = GSF_OUTPUT_STDIO (obj);
386
387         stdio->file = NULL;
388         stdio->create_backup_copy = FALSE;
389         stdio->keep_open          = FALSE;
390 }
391
392 static void
393 gsf_output_stdio_class_init (GObjectClass *gobject_class)
394 {
395         GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
396
397         gobject_class->finalize = gsf_output_stdio_finalize;
398         output_class->Close     = gsf_output_stdio_close;
399         output_class->Seek      = gsf_output_stdio_seek;
400         output_class->Write     = gsf_output_stdio_write;
401         output_class->Vprintf   = gsf_output_stdio_vprintf;
402
403         parent_class = g_type_class_peek_parent (gobject_class);
404 }
405
406 GSF_CLASS (GsfOutputStdio, gsf_output_stdio,
407            gsf_output_stdio_class_init, gsf_output_stdio_init,
408            GSF_OUTPUT_TYPE)
409
410 GsfOutput *
411 gsf_output_stdio_new_valist (char const *filename, GError **err,
412                              char const *first_property_name,
413                              va_list     var_args)
414 {
415         GsfOutputStdio *stdio;
416         FILE *file = NULL;
417         char *dirname = NULL;
418         char *temp_filename = NULL;
419         char *real_filename = follow_symlinks (filename, err);
420         int fd;
421         mode_t saved_umask;
422         struct stat st;
423         gboolean fixup_mode = FALSE;
424
425         if (real_filename == NULL)
426                 goto failure;
427
428         /* Get the directory in which the real filename lives */
429         dirname = g_path_get_dirname (real_filename);
430
431         if (g_stat (real_filename, &st) == 0) {
432                 if (!S_ISREG (st.st_mode)) {
433                         if (err != NULL) {
434                                 char *dname = g_filename_display_name
435                                         (real_filename);
436                                 *err = g_error_new (gsf_output_error_id (), 0,
437                                                     "%s: Is not a regular file",
438                                                     dname);
439                                 g_free (dname);
440                         }
441                         goto failure;
442                 }
443
444                 /* FIXME? Race conditions en masse.  */
445                 if (access_wrapper (real_filename, W_OK) == -1) {
446                         if (err != NULL) {
447                                 int save_errno = errno;
448                                 char *dname = g_filename_display_name
449                                         (real_filename);
450                                 *err = g_error_new
451                                         (gsf_output_error_id (), errno,
452                                          "%s: %s",
453                                          dname, g_strerror (save_errno));
454                                 g_free (dname);
455                         }
456                         goto failure;
457                 }
458         } else {
459                 /*
460                  * File does not exist.  Compute the permissions and uid/gid
461                  * that we will use for the newly-created file.
462                  */
463
464                 memset (&st, 0, sizeof (st));
465
466                 /* Use default permissions */
467                 st.st_mode = 0666;  fixup_mode = TRUE;
468 #ifdef HAVE_CHOWN
469                 {
470                         struct stat dir_st;
471
472                         st.st_uid = getuid ();
473
474                         if (g_stat (dirname, &dir_st) == 0 &&
475                             S_ISDIR (dir_st.st_mode) &&
476                             (dir_st.st_mode & S_ISGID))
477                                 st.st_gid = dir_st.st_gid;
478                         else
479                                 st.st_gid = getgid ();
480                 }
481 #endif
482         }
483
484         /* Save to a temporary file.  We set the umask because some (buggy)
485          * implementations of mkstemp() use permissions 0666 and we want 0600.
486          */
487         temp_filename = g_build_filename (dirname, ".gsf-save-XXXXXX", NULL);
488         /* Oh, joy.  What about threads?  --MW */
489         saved_umask = umask (0077);
490         fd = g_mkstemp (temp_filename); /* this modifies temp_filename to the used name */
491         umask (saved_umask);
492
493         if (fixup_mode)
494                 st.st_mode &= ~saved_umask;
495
496         if (fd < 0 || NULL == (file = fdopen (fd, "wb"))) {
497                 if (err != NULL) {
498                         int save_errno = errno;
499                         char *dname = g_filename_display_name
500                                 (temp_filename);
501                         *err = g_error_new
502                                 (gsf_output_error_id (), errno,
503                                  "%s: %s",
504                                  dname, g_strerror (save_errno));
505                         g_free (dname);
506                 }
507                 goto failure;
508         }
509
510         stdio = (GsfOutputStdio *)g_object_new_valist (GSF_OUTPUT_STDIO_TYPE,
511                 first_property_name, var_args);
512         stdio->file = file;
513         stdio->st = st;
514         stdio->create_backup_copy = FALSE;
515         stdio->real_filename = real_filename;
516         stdio->temp_filename = temp_filename;
517
518         gsf_output_set_name_from_filename (GSF_OUTPUT (stdio), filename);
519
520         g_free (dirname);
521
522         return GSF_OUTPUT (stdio);
523
524  failure:
525         g_free (temp_filename);
526         g_free (real_filename);
527         g_free (dirname);
528         return NULL;
529 }
530
531 /**
532  * gsf_output_stdio_new_full :
533  * @filename : name of file to create or replace.
534  * @err      : optionally %NULL.
535  * @first_property_name : %NULL terminated list of properties
536  * @Varargs : 
537  *
538  * Returns: a new file or %NULL.
539  **/
540 GsfOutput *
541 gsf_output_stdio_new_full (char const *filename, GError **err,
542                            char const *first_property_name, ...)
543 {
544         GsfOutput *res;
545         va_list var_args;
546         
547         va_start (var_args, first_property_name);
548         res = gsf_output_stdio_new_valist (filename, err, first_property_name, var_args);
549         va_end (var_args);
550
551         return res;
552 }
553
554 /**
555  * gsf_output_stdio_new :
556  * @filename : name of file to create or replace.
557  * @err      : optionally %NULL.
558  *
559  * Returns: a new file or %NULL.
560  **/
561 GsfOutput *
562 gsf_output_stdio_new (char const *filename, GError **err)
563 {
564         return gsf_output_stdio_new_full (filename, err, NULL);
565 }
566
567 /**
568  * gsf_output_stdio_new_FILE :
569  * @filename  : The filename corresponding to @file.
570  * @file      : an existing stdio FILE *
571  * @keep_open : Should @file be closed when the wrapper is closed
572  *
573  * Assumes ownership of @file.  If @keep_open is true, ownership reverts
574  * to caller when the GsfObject is closed.
575  *
576  * Returns: a new GsfOutput wrapper for @file.  Warning: the result will be
577  * seekable only if @file is seekable.  If it is seekable, the resulting
578  * GsfOutput object will seek relative to @file's beginning, not its
579  * current location at the time the GsfOutput object is created.
580  **/
581 GsfOutput *
582 gsf_output_stdio_new_FILE (char const *filename, FILE *file, gboolean keep_open)
583 {
584         GsfOutputStdio *stdio;
585
586         g_return_val_if_fail (filename != NULL, NULL);
587         g_return_val_if_fail (file != NULL, NULL);
588
589         stdio = g_object_new (GSF_OUTPUT_STDIO_TYPE, NULL);
590         if (G_UNLIKELY (NULL == stdio)) return NULL;
591
592         stdio->file = file;
593         stdio->keep_open = keep_open;
594         stdio->real_filename = stdio->temp_filename = NULL;
595         gsf_output_set_name_from_filename (GSF_OUTPUT (stdio), filename);
596         return GSF_OUTPUT (stdio);
597 }