Merge remote-tracking branch 'freedesktop/master'
[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   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  */
106 dbus_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  */
211 dbus_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     {
279       if (! _dbus_make_file_world_readable (&tmp_filename, error))
280         goto out;
281     }
282
283   _dbus_verbose ("tmp file %s hnd %p opened\n", tmp_filename_c, hnd);
284
285   need_unlink = TRUE;
286
287   total = 0;
288   bytes_to_write = _dbus_string_get_length (str);
289   str_c = _dbus_string_get_const_data (str);
290
291   while (total < bytes_to_write)
292     {
293       DWORD bytes_written;
294       BOOL res;
295
296       res = WriteFile (hnd, str_c + total, bytes_to_write - total,
297                        &bytes_written, NULL);
298
299       if (res == 0 || bytes_written <= 0)
300         {
301           char *emsg = _dbus_win_error_string (GetLastError ());
302           dbus_set_error (error, _dbus_win_error_from_last_error (),
303                            "Could not write to %s: %s", tmp_filename_c, emsg);
304           _dbus_win_free_error_string (emsg);
305           goto out;
306         }
307
308       total += bytes_written;
309     }
310
311   if (CloseHandle (hnd) == 0)
312     {
313       char *emsg = _dbus_win_error_string (GetLastError ());
314       dbus_set_error (error, _dbus_win_error_from_last_error (),
315                        "Could not close file %s: %s", tmp_filename_c, emsg);
316       _dbus_win_free_error_string (emsg);
317       goto out;
318     }
319
320   hnd = INVALID_HANDLE_VALUE;
321
322   /* Unlike rename(), MoveFileEx() can replace existing files */
323   if (!MoveFileExA (tmp_filename_c, filename_c, MOVEFILE_REPLACE_EXISTING))
324     {
325       char *emsg = _dbus_win_error_string (GetLastError ());
326       dbus_set_error (error, _dbus_win_error_from_last_error (),
327                        "Could not rename %s to %s: %s",
328                        tmp_filename_c, filename_c, emsg);
329       _dbus_win_free_error_string (emsg);
330
331       goto out;
332     }
333
334   need_unlink = FALSE;
335
336   retval = TRUE;
337
338  out:
339   /* close first, then unlink */
340
341   if (hnd != INVALID_HANDLE_VALUE)
342     CloseHandle (hnd);
343
344   if (need_unlink && DeleteFileA (tmp_filename_c) == 0)
345     {
346       char *emsg = _dbus_win_error_string (GetLastError ());
347       _dbus_verbose ("Failed to unlink temp file %s: %s", tmp_filename_c,
348                      emsg);
349       _dbus_win_free_error_string (emsg);
350     }
351
352   _dbus_string_free (&tmp_filename);
353
354   if (!retval)
355     _DBUS_ASSERT_ERROR_IS_SET (error);
356
357   return retval;
358 }
359
360
361 /** Creates the given file, failing if the file already exists.
362  *
363  * @param filename the filename
364  * @param error error location
365  * @returns #TRUE if we created the file and it didn't exist
366  */
367 dbus_bool_t
368 _dbus_create_file_exclusively (const DBusString *filename,
369                                DBusError        *error)
370 {
371   HANDLE hnd;
372   const char *filename_c;
373
374   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
375
376   filename_c = _dbus_string_get_const_data (filename);
377
378   hnd = CreateFileA (filename_c, GENERIC_WRITE,
379                      FILE_SHARE_READ | FILE_SHARE_WRITE,
380                      NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
381                      INVALID_HANDLE_VALUE);
382   if (hnd == INVALID_HANDLE_VALUE)
383     {
384       char *emsg = _dbus_win_error_string (GetLastError ());
385       dbus_set_error (error, _dbus_win_error_from_last_error (),
386                        "Could not create file %s: %s",
387                        filename_c, emsg);
388       _dbus_win_free_error_string (emsg);
389       return FALSE;
390     }
391
392   _dbus_verbose ("exclusive file %s hnd %p opened\n", filename_c, hnd);
393
394   if (CloseHandle (hnd) == 0)
395     {
396       char *emsg = _dbus_win_error_string (GetLastError ());
397       dbus_set_error (error, _dbus_win_error_from_last_error (),
398                        "Could not close file %s: %s",
399                        filename_c, emsg);
400       _dbus_win_free_error_string (emsg);
401
402       return FALSE;
403     }
404
405   return TRUE;
406 }
407