1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2/* dbus-file-win.c windows related file implementation (internal to D-Bus implementation) 3 * 4 * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. 5 * Copyright (C) 2003 CodeFactory AB 6 * 7 * Licensed under the Academic Free License version 2.1 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 */ 24 25#include <config.h> 26#include "dbus-protocol.h" 27#include "dbus-string.h" 28#include "dbus-internals.h" 29#include "dbus-sysdeps-win.h" 30#include "dbus-pipe.h" 31 32#include <windows.h> 33 34 35/** 36 * Thin wrapper around the read() system call that appends 37 * the data it reads to the DBusString buffer. It appends 38 * up to the given count. 39 * 40 * @param hnd the HANDLE to read from 41 * @param buffer the buffer to append data to 42 * @param count the amount of data to read 43 * @param error place to set an error 44 * @returns the number of bytes read or -1 45 */ 46static int 47_dbus_file_read (HANDLE hnd, 48 DBusString *buffer, 49 int count, 50 DBusError *error) 51{ 52 BOOL result; 53 DWORD bytes_read; 54 int start; 55 char *data; 56 57 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 58 59 _dbus_assert (count >= 0); 60 61 start = _dbus_string_get_length (buffer); 62 63 if (!_dbus_string_lengthen (buffer, count)) 64 { 65 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 66 return -1; 67 } 68 69 data = _dbus_string_get_data_len (buffer, start, count); 70 71 result = ReadFile (hnd, data, count, &bytes_read, NULL); 72 if (result == 0) 73 { 74 char *emsg = _dbus_win_error_string (GetLastError ()); 75 dbus_set_error (error, _dbus_win_error_from_last_error (), 76 "Failed to read from 0x%x: %s", hnd, emsg); 77 _dbus_win_free_error_string (emsg); 78 return -1; 79 } 80 81 if (bytes_read) 82 { 83 /* put length back (doesn't actually realloc) */ 84 _dbus_string_set_length (buffer, start + bytes_read); 85 86#if 0 87 if (bytes_read > 0) 88 _dbus_verbose_bytes_of_string (buffer, start, bytes_read); 89#endif 90 } 91 92 return bytes_read; 93} 94 95 96/** 97 * Appends the contents of the given file to the string, 98 * returning error code. At the moment, won't open a file 99 * more than a megabyte in size. 100 * 101 * @param str the string to append to 102 * @param filename filename to load 103 * @param error place to set an error 104 * @returns #FALSE if error was set 105 */ 106dbus_bool_t 107_dbus_file_get_contents (DBusString *str, 108 const DBusString *filename, 109 DBusError *error) 110{ 111 HANDLE hnd; 112 DWORD fsize; 113 DWORD fsize_hi; 114 int orig_len; 115 unsigned int total; 116 const char *filename_c; 117 118 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 119 120 filename_c = _dbus_string_get_const_data (filename); 121 122 hnd = CreateFileA (filename_c, GENERIC_READ, 123 FILE_SHARE_READ | FILE_SHARE_WRITE, 124 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 125 if (hnd == INVALID_HANDLE_VALUE) 126 { 127 char *emsg = _dbus_win_error_string (GetLastError ()); 128 dbus_set_error (error, _dbus_win_error_from_last_error (), 129 "Failed to open \"%s\": %s", filename_c, emsg); 130 _dbus_win_free_error_string (emsg); 131 return FALSE; 132 } 133 134 _dbus_verbose ("file %s hnd %p opened\n", filename_c, hnd); 135 136 fsize = GetFileSize (hnd, &fsize_hi); 137 if (fsize == 0xFFFFFFFF && GetLastError() != NO_ERROR) 138 { 139 char *emsg = _dbus_win_error_string (GetLastError ()); 140 dbus_set_error (error, _dbus_win_error_from_last_error (), 141 "Failed to get file size for \"%s\": %s", 142 filename_c, emsg); 143 _dbus_win_free_error_string (emsg); 144 145 _dbus_verbose ("GetFileSize() failed: %s", emsg); 146 147 CloseHandle (hnd); 148 149 return FALSE; 150 } 151 152 if (fsize_hi != 0 || fsize > _DBUS_ONE_MEGABYTE) 153 { 154 dbus_set_error (error, DBUS_ERROR_FAILED, 155 "File size %lu/%lu of \"%s\" is too large.", 156 (unsigned long) fsize_hi, 157 (unsigned long) fsize, filename_c); 158 CloseHandle (hnd); 159 return FALSE; 160 } 161 162 total = 0; 163 orig_len = _dbus_string_get_length (str); 164 if (fsize > 0) 165 { 166 int bytes_read; 167 168 while (total < fsize) 169 { 170 bytes_read = _dbus_file_read (hnd, str, fsize - total, error); 171 if (bytes_read <= 0) 172 { 173 if (bytes_read == 0) 174 { 175 dbus_set_error (error, DBUS_ERROR_FAILED, 176 "Premature EOF reading \"%s\"", 177 filename_c); 178 } 179 else 180 _DBUS_ASSERT_ERROR_IS_SET (error); 181 182 CloseHandle (hnd); 183 _dbus_string_set_length (str, orig_len); 184 return FALSE; 185 } 186 else 187 total += bytes_read; 188 } 189 190 CloseHandle (hnd); 191 return TRUE; 192 } 193 else 194 { 195 CloseHandle (hnd); 196 return TRUE; 197 } 198} 199 200 201/** 202 * Writes a string out to a file. If the file exists, 203 * it will be atomically overwritten by the new data. 204 * 205 * @param str the string to write out 206 * @param filename the file to save string to 207 * @param world_readable if true, ensure file is world readable 208 * @param error error to be filled in on failure 209 * @returns #FALSE on failure 210 */ 211dbus_bool_t 212_dbus_string_save_to_file (const DBusString *str, 213 const DBusString *filename, 214 dbus_bool_t world_readable, 215 DBusError *error) 216{ 217 HANDLE hnd; 218 int bytes_to_write; 219 const char *filename_c; 220 DBusString tmp_filename; 221 const char *tmp_filename_c; 222 int total; 223 const char *str_c; 224 dbus_bool_t need_unlink; 225 dbus_bool_t retval; 226 227 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 228 229 hnd = INVALID_HANDLE_VALUE; 230 retval = FALSE; 231 need_unlink = FALSE; 232 233 if (!_dbus_string_init (&tmp_filename)) 234 { 235 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 236 return FALSE; 237 } 238 239 if (!_dbus_string_copy (filename, 0, &tmp_filename, 0)) 240 { 241 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 242 _dbus_string_free (&tmp_filename); 243 return FALSE; 244 } 245 246 if (!_dbus_string_append (&tmp_filename, ".")) 247 { 248 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 249 _dbus_string_free (&tmp_filename); 250 return FALSE; 251 } 252 253#define N_TMP_FILENAME_RANDOM_BYTES 8 254 if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES)) 255 { 256 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 257 _dbus_string_free (&tmp_filename); 258 return FALSE; 259 } 260 261 filename_c = _dbus_string_get_const_data (filename); 262 tmp_filename_c = _dbus_string_get_const_data (&tmp_filename); 263 264 /* TODO - support world-readable in an atomic fashion */ 265 hnd = CreateFileA (tmp_filename_c, GENERIC_WRITE, 266 FILE_SHARE_READ | FILE_SHARE_WRITE, 267 NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 268 INVALID_HANDLE_VALUE); 269 if (hnd == INVALID_HANDLE_VALUE) 270 { 271 char *emsg = _dbus_win_error_string (GetLastError ()); 272 dbus_set_error (error, _dbus_win_error_from_last_error (), 273 "Could not create \"%s\": %s", filename_c, emsg); 274 _dbus_win_free_error_string (emsg); 275 goto out; 276 } 277 if (world_readable) 278 _dbus_make_file_world_readable (tmp_filename_c); 279 280 _dbus_verbose ("tmp file %s hnd %p opened\n", tmp_filename_c, hnd); 281 282 need_unlink = TRUE; 283 284 total = 0; 285 bytes_to_write = _dbus_string_get_length (str); 286 str_c = _dbus_string_get_const_data (str); 287 288 while (total < bytes_to_write) 289 { 290 DWORD bytes_written; 291 BOOL res; 292 293 res = WriteFile (hnd, str_c + total, bytes_to_write - total, 294 &bytes_written, NULL); 295 296 if (res == 0 || bytes_written <= 0) 297 { 298 char *emsg = _dbus_win_error_string (GetLastError ()); 299 dbus_set_error (error, _dbus_win_error_from_last_error (), 300 "Could not write to %s: %s", tmp_filename_c, emsg); 301 _dbus_win_free_error_string (emsg); 302 goto out; 303 } 304 305 total += bytes_written; 306 } 307 308 if (CloseHandle (hnd) == 0) 309 { 310 char *emsg = _dbus_win_error_string (GetLastError ()); 311 dbus_set_error (error, _dbus_win_error_from_last_error (), 312 "Could not close file %s: %s", tmp_filename_c, emsg); 313 _dbus_win_free_error_string (emsg); 314 goto out; 315 } 316 317 hnd = INVALID_HANDLE_VALUE; 318 319 /* Unlike rename(), MoveFileEx() can replace existing files */ 320 if (!MoveFileExA (tmp_filename_c, filename_c, MOVEFILE_REPLACE_EXISTING)) 321 { 322 char *emsg = _dbus_win_error_string (GetLastError ()); 323 dbus_set_error (error, _dbus_win_error_from_last_error (), 324 "Could not rename %s to %s: %s", 325 tmp_filename_c, filename_c, emsg); 326 _dbus_win_free_error_string (emsg); 327 328 goto out; 329 } 330 331 need_unlink = FALSE; 332 333 retval = TRUE; 334 335 out: 336 /* close first, then unlink */ 337 338 if (hnd != INVALID_HANDLE_VALUE) 339 CloseHandle (hnd); 340 341 if (need_unlink && DeleteFileA (tmp_filename_c) == 0) 342 { 343 char *emsg = _dbus_win_error_string (GetLastError ()); 344 _dbus_verbose ("Failed to unlink temp file %s: %s", tmp_filename_c, 345 emsg); 346 _dbus_win_free_error_string (emsg); 347 } 348 349 _dbus_string_free (&tmp_filename); 350 351 if (!retval) 352 _DBUS_ASSERT_ERROR_IS_SET (error); 353 354 return retval; 355} 356 357 358/** Creates the given file, failing if the file already exists. 359 * 360 * @param filename the filename 361 * @param error error location 362 * @returns #TRUE if we created the file and it didn't exist 363 */ 364dbus_bool_t 365_dbus_create_file_exclusively (const DBusString *filename, 366 DBusError *error) 367{ 368 HANDLE hnd; 369 const char *filename_c; 370 371 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 372 373 filename_c = _dbus_string_get_const_data (filename); 374 375 hnd = CreateFileA (filename_c, GENERIC_WRITE, 376 FILE_SHARE_READ | FILE_SHARE_WRITE, 377 NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 378 INVALID_HANDLE_VALUE); 379 if (hnd == INVALID_HANDLE_VALUE) 380 { 381 char *emsg = _dbus_win_error_string (GetLastError ()); 382 dbus_set_error (error, _dbus_win_error_from_last_error (), 383 "Could not create file %s: %s", 384 filename_c, emsg); 385 _dbus_win_free_error_string (emsg); 386 return FALSE; 387 } 388 389 _dbus_verbose ("exclusive file %s hnd %p opened\n", filename_c, hnd); 390 391 if (CloseHandle (hnd) == 0) 392 { 393 char *emsg = _dbus_win_error_string (GetLastError ()); 394 dbus_set_error (error, _dbus_win_error_from_last_error (), 395 "Could not close file %s: %s", 396 filename_c, emsg); 397 _dbus_win_free_error_string (emsg); 398 399 return FALSE; 400 } 401 402 return TRUE; 403} 404 405