| /* -*- 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 <config.h> |
| #include "dbus-protocol.h" |
| #include "dbus-string.h" |
| #include "dbus-internals.h" |
| #include "dbus-sysdeps-win.h" |
| #include "dbus-pipe.h" |
| |
| #include <windows.h> |
| |
| |
| /** |
| * 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; |
| } |
| |