/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-file-unix.c unix related file implementation (internal to D-Bus implementation) * * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. * Copyright (C) 2003 CodeFactory AB * * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "dbus-protocol.h" #include "dbus-errors.h" #include "dbus-file.h" #include "dbus-internals.h" #include "dbus-sysdeps.h" #include "dbus-sysdeps-unix.h" #include #include #include #include #include #ifndef O_BINARY #define O_BINARY 0 #endif /** * Appends the contents of the given file to the string, * returning error code. At the moment, won't open a file * more than a megabyte in size. * * @param str the string to append to * @param filename filename to load * @param error place to set an error * @returns #FALSE if error was set */ dbus_bool_t _dbus_file_get_contents (DBusString *str, const DBusString *filename, DBusError *error) { int fd; struct stat sb; int orig_len; int total; const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); filename_c = _dbus_string_get_const_data (filename); /* O_BINARY useful on Cygwin */ fd = open (filename_c, O_RDONLY | O_BINARY); if (fd < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to open \"%s\": %s", filename_c, _dbus_strerror (errno)); return FALSE; } _dbus_verbose ("file fd %d opened\n", fd); if (fstat (fd, &sb) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to stat \"%s\": %s", filename_c, _dbus_strerror (errno)); _dbus_verbose ("fstat() failed: %s", _dbus_strerror (errno)); _dbus_close (fd, NULL); return FALSE; } if (sb.st_size > _DBUS_ONE_MEGABYTE) { dbus_set_error (error, DBUS_ERROR_FAILED, "File size %lu of \"%s\" is too large.", (unsigned long) sb.st_size, filename_c); _dbus_close (fd, NULL); return FALSE; } total = 0; orig_len = _dbus_string_get_length (str); if (sb.st_size > 0 && S_ISREG (sb.st_mode)) { int bytes_read; while (total < (int) sb.st_size) { bytes_read = _dbus_read (fd, str, sb.st_size - total); if (bytes_read <= 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Error reading \"%s\": %s", filename_c, _dbus_strerror (errno)); _dbus_verbose ("read() failed: %s", _dbus_strerror (errno)); _dbus_close (fd, NULL); _dbus_string_set_length (str, orig_len); return FALSE; } else total += bytes_read; } _dbus_close (fd, NULL); return TRUE; } else if (sb.st_size != 0) { _dbus_verbose ("Can only open regular files at the moment.\n"); dbus_set_error (error, DBUS_ERROR_FAILED, "\"%s\" is not a regular file", filename_c); _dbus_close (fd, NULL); return FALSE; } else { _dbus_close (fd, NULL); return TRUE; } } /** * Writes a string out to a file. If the file exists, * it will be atomically overwritten by the new data. * * @param str the string to write out * @param filename the file to save string to * @param world_readable If set, ensure the file is world readable * @param error error to be filled in on failure * @returns #FALSE on failure */ dbus_bool_t _dbus_string_save_to_file (const DBusString *str, const DBusString *filename, dbus_bool_t world_readable, DBusError *error) { int fd; int bytes_to_write; const char *filename_c; DBusString tmp_filename; const char *tmp_filename_c; int total; dbus_bool_t need_unlink; dbus_bool_t retval; _DBUS_ASSERT_ERROR_IS_CLEAR (error); fd = -1; retval = FALSE; need_unlink = FALSE; if (!_dbus_string_init (&tmp_filename)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return FALSE; } if (!_dbus_string_copy (filename, 0, &tmp_filename, 0)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); _dbus_string_free (&tmp_filename); return FALSE; } if (!_dbus_string_append (&tmp_filename, ".")) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); _dbus_string_free (&tmp_filename); return FALSE; } #define N_TMP_FILENAME_RANDOM_BYTES 8 if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); _dbus_string_free (&tmp_filename); return FALSE; } filename_c = _dbus_string_get_const_data (filename); tmp_filename_c = _dbus_string_get_const_data (&tmp_filename); fd = open (tmp_filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, world_readable ? 0644 : 0600); if (fd < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not create %s: %s", tmp_filename_c, _dbus_strerror (errno)); goto out; } if (world_readable) { /* Ensure the file is world readable even in the presence of * possibly restrictive umasks; * see http://lists.freedesktop.org/archives/dbus/2010-September/013367.html */ if (fchmod (fd, 0644) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not chmod %s: %s", tmp_filename_c, _dbus_strerror (errno)); goto out; } } _dbus_verbose ("tmp file fd %d opened\n", fd); need_unlink = TRUE; total = 0; bytes_to_write = _dbus_string_get_length (str); while (total < bytes_to_write) { int bytes_written; bytes_written = _dbus_write (fd, str, total, bytes_to_write - total); if (bytes_written <= 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not write to %s: %s", tmp_filename_c, _dbus_strerror (errno)); goto out; } total += bytes_written; } if (fsync(fd)) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not synchronize file %s: %s", tmp_filename_c, _dbus_strerror (errno)); goto out; } if (!_dbus_close (fd, NULL)) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not close file %s: %s", tmp_filename_c, _dbus_strerror (errno)); goto out; } fd = -1; if (rename (tmp_filename_c, filename_c) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not rename %s to %s: %s", tmp_filename_c, filename_c, _dbus_strerror (errno)); goto out; } need_unlink = FALSE; retval = TRUE; out: /* close first, then unlink, to prevent ".nfs34234235" garbage * files */ if (fd >= 0) _dbus_close (fd, NULL); if (need_unlink && unlink (tmp_filename_c) < 0) _dbus_verbose ("Failed to unlink temp file %s: %s\n", tmp_filename_c, _dbus_strerror (errno)); _dbus_string_free (&tmp_filename); if (!retval) _DBUS_ASSERT_ERROR_IS_SET (error); return retval; } /** Makes the file readable by every user in the system. * * @param filename the filename * @param error error location * @returns #TRUE if the file's permissions could be changed. */ dbus_bool_t _dbus_make_file_world_readable(const DBusString *filename, DBusError *error) { const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); filename_c = _dbus_string_get_const_data (filename); if (chmod (filename_c, 0644) == -1) { dbus_set_error (error, DBUS_ERROR_FAILED, "Could not change permissions of file %s: %s\n", filename_c, _dbus_strerror (errno)); return FALSE; } return TRUE; } /** Creates the given file, failing if the file already exists. * * @param filename the filename * @param error error location * @returns #TRUE if we created the file and it didn't exist */ dbus_bool_t _dbus_create_file_exclusively (const DBusString *filename, DBusError *error) { int fd; const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); filename_c = _dbus_string_get_const_data (filename); fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, 0600); if (fd < 0) { dbus_set_error (error, DBUS_ERROR_FAILED, "Could not create file %s: %s\n", filename_c, _dbus_strerror (errno)); return FALSE; } _dbus_verbose ("exclusive file fd %d opened\n", fd); if (!_dbus_close (fd, NULL)) { dbus_set_error (error, DBUS_ERROR_FAILED, "Could not close file %s: %s\n", filename_c, _dbus_strerror (errno)); return FALSE; } return TRUE; } /** * Deletes the given file. * * @param filename the filename * @param error error location * * @returns #TRUE if unlink() succeeded */ dbus_bool_t _dbus_delete_file (const DBusString *filename, DBusError *error) { const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); filename_c = _dbus_string_get_const_data (filename); if (unlink (filename_c) < 0) { dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to delete file %s: %s\n", filename_c, _dbus_strerror (errno)); return FALSE; } else return TRUE; }