Merge branch 'dbus-1.2'
[platform/upstream/dbus.git] / dbus / dbus-file-win.c
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  */
46 static 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  again:
72
73   result = ReadFile (hnd, data, count, &bytes_read, NULL);
74   if (result == 0)
75     {
76       char *emsg = _dbus_win_error_string (GetLastError ());
77       dbus_set_error (error, _dbus_win_error_from_last_error (),
78                       "Failed to read from 0x%x: %s", hnd, emsg);
79       _dbus_win_free_error_string (emsg);
80       return -1;
81     }
82
83   if (bytes_read)
84     {
85       /* put length back (doesn't actually realloc) */
86       _dbus_string_set_length (buffer, start + bytes_read);
87
88 #if 0
89       if (bytes_read > 0)
90         _dbus_verbose_bytes_of_string (buffer, start, bytes_read);
91 #endif
92     }
93
94   return bytes_read;
95 }
96
97
98 /**
99  * Appends the contents of the given file to the string,
100  * returning error code. At the moment, won't open a file
101  * more than a megabyte in size.
102  *
103  * @param str the string to append to
104  * @param filename filename to load
105  * @param error place to set an error
106  * @returns #FALSE if error was set
107  */
108 dbus_bool_t
109 _dbus_file_get_contents (DBusString       *str,
110                          const DBusString *filename,
111                          DBusError        *error)
112 {
113   HANDLE hnd;
114   DWORD fsize;
115   DWORD fsize_hi;
116   int orig_len;
117   int total;
118   const char *filename_c;
119
120   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
121
122   filename_c = _dbus_string_get_const_data (filename);
123
124   hnd = CreateFileA (filename_c, GENERIC_READ,
125                     FILE_SHARE_READ | FILE_SHARE_WRITE,
126                     NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
127   if (hnd == INVALID_HANDLE_VALUE)
128     {
129       char *emsg = _dbus_win_error_string (GetLastError ());
130       dbus_set_error (error, _dbus_win_error_from_last_error (),
131                        "Failed to open \"%s\": %s", filename_c, emsg);
132       _dbus_win_free_error_string (emsg);
133       return FALSE;
134     }
135
136   _dbus_verbose ("file %s hnd %p opened\n", filename_c, hnd);
137   
138   fsize = GetFileSize (hnd, &fsize_hi);
139   if (fsize == 0xFFFFFFFF && GetLastError() != NO_ERROR)
140     { 
141       char *emsg = _dbus_win_error_string (GetLastError ());
142       dbus_set_error (error, _dbus_win_error_from_last_error (),
143                       "Failed to get file size for \"%s\": %s",
144                       filename_c, emsg);
145       _dbus_win_free_error_string (emsg);
146
147       _dbus_verbose ("GetFileSize() failed: %s", emsg);
148
149       CloseHandle (hnd);
150
151       return FALSE;
152     }
153
154   if (fsize_hi != 0 || fsize > _DBUS_ONE_MEGABYTE)
155     {
156       dbus_set_error (error, DBUS_ERROR_FAILED,
157                       "File size %lu/%lu of \"%s\" is too large.",
158                       (unsigned long) fsize_hi,
159                       (unsigned long) fsize, filename_c);
160       CloseHandle (hnd);
161       return FALSE;
162     }
163
164   total = 0;
165   orig_len = _dbus_string_get_length (str);
166   if (fsize > 0)
167     {
168       int bytes_read;
169
170       while (total < fsize)
171         {
172           bytes_read = _dbus_file_read (hnd, str, fsize - total, error);
173           if (bytes_read <= 0)
174             {
175               if (bytes_read == 0)
176                 {
177                   dbus_set_error (error, DBUS_ERROR_FAILED,
178                                   "Premature EOF reading \"%s\"",
179                                   filename_c);
180                 }
181               else
182                 _DBUS_ASSERT_ERROR_IS_SET (error);
183
184               CloseHandle (hnd);
185               _dbus_string_set_length (str, orig_len);
186               return FALSE;
187             }
188           else
189             total += bytes_read;
190         }
191
192       CloseHandle (hnd);
193       return TRUE;
194     }
195   else
196     {
197       CloseHandle (hnd);
198       return TRUE;
199     }
200 }
201
202
203 /**
204  * Writes a string out to a file. If the file exists,
205  * it will be atomically overwritten by the new data.
206  *
207  * @param str the string to write out
208  * @param filename the file to save string to
209  * @param error error to be filled in on failure
210  * @returns #FALSE on failure
211  */
212 dbus_bool_t
213 _dbus_string_save_to_file (const DBusString *str,
214                            const DBusString *filename,
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   hnd = CreateFileA (tmp_filename_c, GENERIC_WRITE,
265                      FILE_SHARE_READ | FILE_SHARE_WRITE,
266                      NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
267                      INVALID_HANDLE_VALUE);
268   if (hnd == INVALID_HANDLE_VALUE)
269     {
270       char *emsg = _dbus_win_error_string (GetLastError ());
271       dbus_set_error (error, _dbus_win_error_from_last_error (),
272                        "Could not create \"%s\": %s", filename_c, emsg);
273       _dbus_win_free_error_string (emsg);
274       goto out;
275     }
276
277   _dbus_verbose ("tmp file %s hnd %p opened\n", tmp_filename_c, hnd);
278
279   need_unlink = TRUE;
280
281   total = 0;
282   bytes_to_write = _dbus_string_get_length (str);
283   str_c = _dbus_string_get_const_data (str);
284
285   while (total < bytes_to_write)
286     {
287       DWORD bytes_written;
288       BOOL res;
289
290       res = WriteFile (hnd, str_c + total, bytes_to_write - total,
291                        &bytes_written, NULL);
292
293       if (res == 0 || bytes_written <= 0)
294         {
295           char *emsg = _dbus_win_error_string (GetLastError ());
296           dbus_set_error (error, _dbus_win_error_from_last_error (),
297                            "Could not write to %s: %s", tmp_filename_c, emsg);
298           _dbus_win_free_error_string (emsg);
299           goto out;
300         }
301
302       total += bytes_written;
303     }
304
305   if (CloseHandle (hnd) == 0)
306     {
307       char *emsg = _dbus_win_error_string (GetLastError ());
308       dbus_set_error (error, _dbus_win_error_from_last_error (),
309                        "Could not close file %s: %s", tmp_filename_c, emsg);
310       _dbus_win_free_error_string (emsg);
311       goto out;
312     }
313
314   hnd = INVALID_HANDLE_VALUE;
315
316   /* Unlike rename(), MoveFileEx() can replace existing files */
317   if (!MoveFileExA (tmp_filename_c, filename_c, MOVEFILE_REPLACE_EXISTING))
318     {
319       char *emsg = _dbus_win_error_string (GetLastError ());
320       dbus_set_error (error, _dbus_win_error_from_last_error (),
321                        "Could not rename %s to %s: %s",
322                        tmp_filename_c, filename_c, emsg);
323       _dbus_win_free_error_string (emsg);
324
325       goto out;
326     }
327
328   need_unlink = FALSE;
329
330   retval = TRUE;
331
332  out:
333   /* close first, then unlink */
334
335   if (hnd != INVALID_HANDLE_VALUE)
336     CloseHandle (hnd);
337
338   if (need_unlink && DeleteFileA (tmp_filename_c) == 0)
339     {
340       char *emsg = _dbus_win_error_string (GetLastError ());
341       _dbus_verbose ("Failed to unlink temp file %s: %s", tmp_filename_c,
342                      emsg);
343       _dbus_win_free_error_string (emsg);
344     }
345
346   _dbus_string_free (&tmp_filename);
347
348   if (!retval)
349     _DBUS_ASSERT_ERROR_IS_SET (error);
350
351   return retval;
352 }
353
354
355 /** Creates the given file, failing if the file already exists.
356  *
357  * @param filename the filename
358  * @param error error location
359  * @returns #TRUE if we created the file and it didn't exist
360  */
361 dbus_bool_t
362 _dbus_create_file_exclusively (const DBusString *filename,
363                                DBusError        *error)
364 {
365   HANDLE hnd;
366   const char *filename_c;
367
368   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
369
370   filename_c = _dbus_string_get_const_data (filename);
371
372   hnd = CreateFileA (filename_c, GENERIC_WRITE,
373                      FILE_SHARE_READ | FILE_SHARE_WRITE,
374                      NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
375                      INVALID_HANDLE_VALUE);
376   if (hnd == INVALID_HANDLE_VALUE)
377     {
378       char *emsg = _dbus_win_error_string (GetLastError ());
379       dbus_set_error (error, _dbus_win_error_from_last_error (),
380                        "Could not create file %s: %s",
381                        filename_c, emsg);
382       _dbus_win_free_error_string (emsg);
383       return FALSE;
384     }
385
386   _dbus_verbose ("exclusive file %s hnd %p opened\n", filename_c, hnd);
387
388   if (CloseHandle (hnd) == 0)
389     {
390       char *emsg = _dbus_win_error_string (GetLastError ());
391       dbus_set_error (error, _dbus_win_error_from_last_error (),
392                        "Could not close file %s: %s",
393                        filename_c, emsg);
394       _dbus_win_free_error_string (emsg);
395
396       return FALSE;
397     }
398
399   return TRUE;
400 }
401