/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-file-win.c windows 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-string.h" #include "dbus-internals.h" #include "dbus-sysdeps-win.h" #include "dbus-pipe.h" #include /** * Thin wrapper around the read() system call that appends * the data it reads to the DBusString buffer. It appends * up to the given count. * * @param hnd the HANDLE to read from * @param buffer the buffer to append data to * @param count the amount of data to read * @param error place to set an error * @returns the number of bytes read or -1 */ static int _dbus_file_read (HANDLE hnd, DBusString *buffer, int count, DBusError *error) { BOOL result; DWORD bytes_read; int start; char *data; _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_assert (count >= 0); start = _dbus_string_get_length (buffer); if (!_dbus_string_lengthen (buffer, count)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return -1; } data = _dbus_string_get_data_len (buffer, start, count); result = ReadFile (hnd, data, count, &bytes_read, NULL); if (result == 0) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Failed to read from 0x%x: %s", hnd, emsg); _dbus_win_free_error_string (emsg); return -1; } if (bytes_read) { /* put length back (doesn't actually realloc) */ _dbus_string_set_length (buffer, start + bytes_read); #if 0 if (bytes_read > 0) _dbus_verbose_bytes_of_string (buffer, start, bytes_read); #endif } return bytes_read; } /** * 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) { HANDLE hnd; DWORD fsize; DWORD fsize_hi; int orig_len; unsigned int total; const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); filename_c = _dbus_string_get_const_data (filename); hnd = CreateFileA (filename_c, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hnd == INVALID_HANDLE_VALUE) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Failed to open \"%s\": %s", filename_c, emsg); _dbus_win_free_error_string (emsg); return FALSE; } _dbus_verbose ("file %s hnd %p opened\n", filename_c, hnd); fsize = GetFileSize (hnd, &fsize_hi); if (fsize == 0xFFFFFFFF && GetLastError() != NO_ERROR) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Failed to get file size for \"%s\": %s", filename_c, emsg); _dbus_win_free_error_string (emsg); _dbus_verbose ("GetFileSize() failed: %s", emsg); CloseHandle (hnd); return FALSE; } if (fsize_hi != 0 || fsize > _DBUS_ONE_MEGABYTE) { dbus_set_error (error, DBUS_ERROR_FAILED, "File size %lu/%lu of \"%s\" is too large.", (unsigned long) fsize_hi, (unsigned long) fsize, filename_c); CloseHandle (hnd); return FALSE; } total = 0; orig_len = _dbus_string_get_length (str); if (fsize > 0) { int bytes_read; while (total < fsize) { bytes_read = _dbus_file_read (hnd, str, fsize - total, error); if (bytes_read <= 0) { if (bytes_read == 0) { dbus_set_error (error, DBUS_ERROR_FAILED, "Premature EOF reading \"%s\"", filename_c); } else _DBUS_ASSERT_ERROR_IS_SET (error); CloseHandle (hnd); _dbus_string_set_length (str, orig_len); return FALSE; } else total += bytes_read; } CloseHandle (hnd); return TRUE; } else { CloseHandle (hnd); 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 true, ensure 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) { HANDLE hnd; int bytes_to_write; const char *filename_c; DBusString tmp_filename; const char *tmp_filename_c; int total; const char *str_c; dbus_bool_t need_unlink; dbus_bool_t retval; _DBUS_ASSERT_ERROR_IS_CLEAR (error); hnd = INVALID_HANDLE_VALUE; 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); /* TODO - support world-readable in an atomic fashion */ hnd = CreateFileA (tmp_filename_c, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (hnd == INVALID_HANDLE_VALUE) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Could not create \"%s\": %s", filename_c, emsg); _dbus_win_free_error_string (emsg); goto out; } if (world_readable) { if (! _dbus_make_file_world_readable (&tmp_filename, error)) goto out; } _dbus_verbose ("tmp file %s hnd %p opened\n", tmp_filename_c, hnd); need_unlink = TRUE; total = 0; bytes_to_write = _dbus_string_get_length (str); str_c = _dbus_string_get_const_data (str); while (total < bytes_to_write) { DWORD bytes_written; BOOL res; res = WriteFile (hnd, str_c + total, bytes_to_write - total, &bytes_written, NULL); if (res == 0 || bytes_written <= 0) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Could not write to %s: %s", tmp_filename_c, emsg); _dbus_win_free_error_string (emsg); goto out; } total += bytes_written; } if (CloseHandle (hnd) == 0) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Could not close file %s: %s", tmp_filename_c, emsg); _dbus_win_free_error_string (emsg); goto out; } hnd = INVALID_HANDLE_VALUE; /* Unlike rename(), MoveFileEx() can replace existing files */ if (!MoveFileExA (tmp_filename_c, filename_c, MOVEFILE_REPLACE_EXISTING)) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Could not rename %s to %s: %s", tmp_filename_c, filename_c, emsg); _dbus_win_free_error_string (emsg); goto out; } need_unlink = FALSE; retval = TRUE; out: /* close first, then unlink */ if (hnd != INVALID_HANDLE_VALUE) CloseHandle (hnd); if (need_unlink && DeleteFileA (tmp_filename_c) == 0) { char *emsg = _dbus_win_error_string (GetLastError ()); _dbus_verbose ("Failed to unlink temp file %s: %s", tmp_filename_c, emsg); _dbus_win_free_error_string (emsg); } _dbus_string_free (&tmp_filename); if (!retval) _DBUS_ASSERT_ERROR_IS_SET (error); return retval; } /** 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) { HANDLE hnd; const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); filename_c = _dbus_string_get_const_data (filename); hnd = CreateFileA (filename_c, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (hnd == INVALID_HANDLE_VALUE) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Could not create file %s: %s", filename_c, emsg); _dbus_win_free_error_string (emsg); return FALSE; } _dbus_verbose ("exclusive file %s hnd %p opened\n", filename_c, hnd); if (CloseHandle (hnd) == 0) { char *emsg = _dbus_win_error_string (GetLastError ()); dbus_set_error (error, _dbus_win_error_from_last_error (), "Could not close file %s: %s", filename_c, emsg); _dbus_win_free_error_string (emsg); return FALSE; } return TRUE; }