1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * gsf-output-stdio.c: stdio based output
5 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
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.
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.
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
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>
35 #include <sys/types.h>
37 #ifdef HAVE_SYS_STATFS_H
38 #include <sys/statfs.h>
48 #endif /* G_OS_WIN32 */
54 static GObjectClass *parent_class;
56 struct _GsfOutputStdio {
60 char *real_filename, *temp_filename;
61 gboolean create_backup_copy, keep_open;
66 GsfOutputClass output_class;
67 } GsfOutputStdioClass;
70 rename_wrapper (char const *oldfilename, char const *newfilename)
72 int result = g_rename (oldfilename, newfilename);
76 #ifdef HAVE_SYS_STATFS_H
77 #ifdef HAVE_2ARG_STATFS
78 /* The FUSE file system does not unlink the target. */
80 int save_errno = errno;
82 if (statfs (newfilename, &buf) == 0 &&
83 memcmp (&buf.f_type, "FUse", 4) == 0)
84 goto unlink_and_retry;
91 /* Win32's rename does not unlink the target. */
92 goto unlink_and_retry;
99 (void)g_unlink (newfilename);
100 result = g_rename (oldfilename, newfilename);
105 chmod_wrapper (const char *filename, mode_t mode)
108 return g_chmod (filename, mode);
110 return chmod (filename, mode);
115 access_wrapper (char const *filename, int what)
118 return g_access (filename, what);
120 return access (filename, what);
124 #define GSF_MAX_LINK_LEVEL 256
126 /* Calls g_file_read_link() until we find a real filename. */
128 follow_symlinks (char const *filename, GError **error)
130 gchar *followed_filename, *link;
133 g_return_val_if_fail (filename != NULL, NULL);
135 followed_filename = g_strdup (filename);
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;
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);
156 return followed_filename;
158 /* Too many symlinks */
163 /* We have links, but not ELOOP. Very strange. */
166 *error = g_error_new_literal (gsf_output_error_id (), err,
169 g_free (followed_filename);
174 close_file_helper (GsfOutputStdio *stdio, gboolean seterr)
176 gboolean res = (0 == fclose (stdio->file));
179 gsf_output_set_error (GSF_OUTPUT (stdio), errno,
180 "Failed to close file: %s",
186 unlink_file_helper (GsfOutputStdio *stdio)
188 if (!stdio->temp_filename)
191 if (g_unlink (stdio->temp_filename) == 0) {
192 g_free (stdio->temp_filename);
193 stdio->temp_filename = NULL;
201 gsf_output_stdio_close (GsfOutput *output)
203 GsfOutputStdio *stdio = GSF_OUTPUT_STDIO (output);
205 char *backup_filename = NULL;
207 if (stdio->file == NULL)
210 if (gsf_output_error (output)) {
212 if (!stdio->keep_open && !close_file_helper (stdio, FALSE))
215 if (!unlink_file_helper (stdio))
221 if (stdio->keep_open) {
222 gboolean res = (0 == fflush (stdio->file));
224 gsf_output_set_error (output, errno,
230 res = close_file_helper (stdio, TRUE);
232 /* short circuit our when dealing with raw FILE */
233 if (!stdio->real_filename)
236 unlink_file_helper (stdio);
240 /* Move the original file to a backup */
241 if (stdio->create_backup_copy) {
243 backup_filename = g_strconcat (stdio->real_filename, ".bak", NULL);
244 result = rename_wrapper (stdio->real_filename, backup_filename);
246 char *utf8name = g_filename_display_name (backup_filename);
247 gsf_output_set_error (output, errno,
248 "Could not backup the original as %s.",
251 g_free (backup_filename);
252 g_unlink (stdio->temp_filename);
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)
263 res = gsf_output_set_error (output,
265 "%s", g_strerror (saved_errno));
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.
271 chmod_wrapper (stdio->real_filename, stdio->st.st_mode);
273 if (chown (stdio->real_filename,
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);
280 chmod_wrapper (stdio->real_filename, stdio->st.st_mode);
284 g_free (backup_filename);
290 gsf_output_stdio_finalize (GObject *obj)
292 GsfOutput *output = (GsfOutput *)obj;
293 GsfOutputStdio *stdio = GSF_OUTPUT_STDIO (output);
295 if (!gsf_output_is_closed (output))
296 gsf_output_close (output);
298 g_free (stdio->real_filename);
299 stdio->real_filename = NULL;
300 g_free (stdio->temp_filename);
301 stdio->temp_filename = NULL;
303 parent_class->finalize (obj);
307 gsf_output_stdio_seek (GsfOutput *output, gsf_off_t offset, GSeekType whence)
309 GsfOutputStdio const *stdio = GSF_OUTPUT_STDIO (output);
310 int stdio_whence = 0; /* make compiler shut up */
318 g_return_val_if_fail (stdio->file != NULL,
319 gsf_output_set_error (output, 0, "missing file"));
322 if ((gsf_off_t) loffset != offset) { /* Check for overflow */
324 g_warning ("offset too large for fseeko");
325 return gsf_output_set_error (output, 0, "offset too large for fseeko");
327 g_warning ("offset too large for fseek");
328 return gsf_output_set_error (output, 0, "offset too large for fseek");
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;
340 if (0 == fseeko (stdio->file, loffset, stdio_whence))
343 if (0 == fseek (stdio->file, loffset, stdio_whence))
346 return gsf_output_set_error (output, errno, "%s", g_strerror (errno));
350 gsf_output_stdio_write (GsfOutput *output,
352 guint8 const *buffer)
354 GsfOutputStdio *stdio = GSF_OUTPUT_STDIO (output);
355 size_t written, remaining;
357 g_return_val_if_fail (stdio != NULL, FALSE);
358 g_return_val_if_fail (stdio->file != NULL, FALSE);
360 remaining = num_bytes;
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));
368 remaining -= written;
373 static gsf_off_t gsf_output_stdio_vprintf (GsfOutput *output,
374 char const *fmt, va_list args) G_GNUC_PRINTF (2, 0);
377 gsf_output_stdio_vprintf (GsfOutput *output, char const *fmt, va_list args)
379 return vfprintf (((GsfOutputStdio *)output)->file, fmt, args);
383 gsf_output_stdio_init (GObject *obj)
385 GsfOutputStdio *stdio = GSF_OUTPUT_STDIO (obj);
388 stdio->create_backup_copy = FALSE;
389 stdio->keep_open = FALSE;
393 gsf_output_stdio_class_init (GObjectClass *gobject_class)
395 GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
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;
403 parent_class = g_type_class_peek_parent (gobject_class);
406 GSF_CLASS (GsfOutputStdio, gsf_output_stdio,
407 gsf_output_stdio_class_init, gsf_output_stdio_init,
411 gsf_output_stdio_new_valist (char const *filename, GError **err,
412 char const *first_property_name,
415 GsfOutputStdio *stdio;
417 char *dirname = NULL;
418 char *temp_filename = NULL;
419 char *real_filename = follow_symlinks (filename, err);
423 gboolean fixup_mode = FALSE;
425 if (real_filename == NULL)
428 /* Get the directory in which the real filename lives */
429 dirname = g_path_get_dirname (real_filename);
431 if (g_stat (real_filename, &st) == 0) {
432 if (!S_ISREG (st.st_mode)) {
434 char *dname = g_filename_display_name
436 *err = g_error_new (gsf_output_error_id (), 0,
437 "%s: Is not a regular file",
444 /* FIXME? Race conditions en masse. */
445 if (access_wrapper (real_filename, W_OK) == -1) {
447 int save_errno = errno;
448 char *dname = g_filename_display_name
451 (gsf_output_error_id (), errno,
453 dname, g_strerror (save_errno));
460 * File does not exist. Compute the permissions and uid/gid
461 * that we will use for the newly-created file.
464 memset (&st, 0, sizeof (st));
466 /* Use default permissions */
467 st.st_mode = 0666; fixup_mode = TRUE;
472 st.st_uid = getuid ();
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;
479 st.st_gid = getgid ();
484 /* Save to a temporary file. We set the umask because some (buggy)
485 * implementations of mkstemp() use permissions 0666 and we want 0600.
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 */
494 st.st_mode &= ~saved_umask;
496 if (fd < 0 || NULL == (file = fdopen (fd, "wb"))) {
498 int save_errno = errno;
499 char *dname = g_filename_display_name
502 (gsf_output_error_id (), errno,
504 dname, g_strerror (save_errno));
510 stdio = (GsfOutputStdio *)g_object_new_valist (GSF_OUTPUT_STDIO_TYPE,
511 first_property_name, var_args);
514 stdio->create_backup_copy = FALSE;
515 stdio->real_filename = real_filename;
516 stdio->temp_filename = temp_filename;
518 gsf_output_set_name_from_filename (GSF_OUTPUT (stdio), filename);
522 return GSF_OUTPUT (stdio);
525 g_free (temp_filename);
526 g_free (real_filename);
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
538 * Returns: a new file or %NULL.
541 gsf_output_stdio_new_full (char const *filename, GError **err,
542 char const *first_property_name, ...)
547 va_start (var_args, first_property_name);
548 res = gsf_output_stdio_new_valist (filename, err, first_property_name, var_args);
555 * gsf_output_stdio_new :
556 * @filename : name of file to create or replace.
557 * @err : optionally %NULL.
559 * Returns: a new file or %NULL.
562 gsf_output_stdio_new (char const *filename, GError **err)
564 return gsf_output_stdio_new_full (filename, err, NULL);
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
573 * Assumes ownership of @file. If @keep_open is true, ownership reverts
574 * to caller when the GsfObject is closed.
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.
582 gsf_output_stdio_new_FILE (char const *filename, FILE *file, gboolean keep_open)
584 GsfOutputStdio *stdio;
586 g_return_val_if_fail (filename != NULL, NULL);
587 g_return_val_if_fail (file != NULL, NULL);
589 stdio = g_object_new (GSF_OUTPUT_STDIO_TYPE, NULL);
590 if (G_UNLIKELY (NULL == stdio)) return NULL;
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);