/src/glib/gio/glocalfileoutputstream.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GIO - GLib Input, Output and Streaming Library |
2 | | * |
3 | | * Copyright (C) 2006-2007 Red Hat, Inc. |
4 | | * |
5 | | * This library is free software; you can redistribute it and/or |
6 | | * modify it under the terms of the GNU Lesser General Public |
7 | | * License as published by the Free Software Foundation; either |
8 | | * version 2.1 of the License, or (at your option) any later version. |
9 | | * |
10 | | * This library is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | * Lesser General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU Lesser General |
16 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | | * |
18 | | * Author: Alexander Larsson <alexl@redhat.com> |
19 | | */ |
20 | | |
21 | | /* Needed for the statx() calls in inline functions in glocalfileinfo.h */ |
22 | | #ifndef _GNU_SOURCE |
23 | | #define _GNU_SOURCE |
24 | | #endif |
25 | | |
26 | | #include "config.h" |
27 | | |
28 | | #include <sys/types.h> |
29 | | #include <sys/stat.h> |
30 | | #include <fcntl.h> |
31 | | #include <errno.h> |
32 | | #include <string.h> |
33 | | |
34 | | #include <glib.h> |
35 | | #include <glib/gstdio.h> |
36 | | #include "glibintl.h" |
37 | | #include "gioerror.h" |
38 | | #include "gcancellable.h" |
39 | | #include "glocalfileoutputstream.h" |
40 | | #include "gfileinfo.h" |
41 | | #include "glocalfileinfo.h" |
42 | | |
43 | | #ifdef G_OS_UNIX |
44 | | #include <unistd.h> |
45 | | #include "gfiledescriptorbased.h" |
46 | | #include <sys/uio.h> |
47 | | #endif |
48 | | |
49 | | #include "glib-private.h" |
50 | | #include "gioprivate.h" |
51 | | |
52 | | #ifdef G_OS_WIN32 |
53 | | #include <io.h> |
54 | | #ifndef S_ISDIR |
55 | | #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) |
56 | | #endif |
57 | | #ifndef S_ISREG |
58 | | #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) |
59 | | #endif |
60 | | #endif |
61 | | |
62 | | #ifndef O_BINARY |
63 | 0 | #define O_BINARY 0 |
64 | | #endif |
65 | | |
66 | | #ifndef O_CLOEXEC |
67 | | #define O_CLOEXEC 0 |
68 | | #else |
69 | | #define HAVE_O_CLOEXEC 1 |
70 | | #endif |
71 | | |
72 | | struct _GLocalFileOutputStreamPrivate { |
73 | | char *tmp_filename; |
74 | | char *original_filename; |
75 | | char *backup_filename; |
76 | | char *etag; |
77 | | guint sync_on_close : 1; |
78 | | guint do_close : 1; |
79 | | int fd; |
80 | | }; |
81 | | |
82 | | #ifdef G_OS_UNIX |
83 | | static void g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface); |
84 | | #endif |
85 | | |
86 | | #define g_local_file_output_stream_get_type _g_local_file_output_stream_get_type |
87 | | #ifdef G_OS_UNIX |
88 | | G_DEFINE_TYPE_WITH_CODE (GLocalFileOutputStream, g_local_file_output_stream, G_TYPE_FILE_OUTPUT_STREAM, |
89 | | G_ADD_PRIVATE (GLocalFileOutputStream) |
90 | | G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED, |
91 | | g_file_descriptor_based_iface_init)) |
92 | | #else |
93 | | G_DEFINE_TYPE_WITH_CODE (GLocalFileOutputStream, g_local_file_output_stream, G_TYPE_FILE_OUTPUT_STREAM, |
94 | | G_ADD_PRIVATE (GLocalFileOutputStream)) |
95 | | #endif |
96 | | |
97 | | |
98 | | /* Some of the file replacement code was based on the code from gedit, |
99 | | * relicenced to LGPL with permissions from the authors. |
100 | | */ |
101 | | |
102 | 0 | #define BACKUP_EXTENSION "~" |
103 | | |
104 | | static gssize g_local_file_output_stream_write (GOutputStream *stream, |
105 | | const void *buffer, |
106 | | gsize count, |
107 | | GCancellable *cancellable, |
108 | | GError **error); |
109 | | #ifdef G_OS_UNIX |
110 | | static gboolean g_local_file_output_stream_writev (GOutputStream *stream, |
111 | | const GOutputVector *vectors, |
112 | | gsize n_vectors, |
113 | | gsize *bytes_written, |
114 | | GCancellable *cancellable, |
115 | | GError **error); |
116 | | #endif |
117 | | static gboolean g_local_file_output_stream_close (GOutputStream *stream, |
118 | | GCancellable *cancellable, |
119 | | GError **error); |
120 | | static GFileInfo *g_local_file_output_stream_query_info (GFileOutputStream *stream, |
121 | | const char *attributes, |
122 | | GCancellable *cancellable, |
123 | | GError **error); |
124 | | static char * g_local_file_output_stream_get_etag (GFileOutputStream *stream); |
125 | | static goffset g_local_file_output_stream_tell (GFileOutputStream *stream); |
126 | | static gboolean g_local_file_output_stream_can_seek (GFileOutputStream *stream); |
127 | | static gboolean g_local_file_output_stream_seek (GFileOutputStream *stream, |
128 | | goffset offset, |
129 | | GSeekType type, |
130 | | GCancellable *cancellable, |
131 | | GError **error); |
132 | | static gboolean g_local_file_output_stream_can_truncate (GFileOutputStream *stream); |
133 | | static gboolean g_local_file_output_stream_truncate (GFileOutputStream *stream, |
134 | | goffset size, |
135 | | GCancellable *cancellable, |
136 | | GError **error); |
137 | | #ifdef G_OS_UNIX |
138 | | static int g_local_file_output_stream_get_fd (GFileDescriptorBased *stream); |
139 | | #endif |
140 | | |
141 | | static void |
142 | | g_local_file_output_stream_finalize (GObject *object) |
143 | 0 | { |
144 | 0 | GLocalFileOutputStream *file; |
145 | | |
146 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (object); |
147 | | |
148 | 0 | g_free (file->priv->tmp_filename); |
149 | 0 | g_free (file->priv->original_filename); |
150 | 0 | g_free (file->priv->backup_filename); |
151 | 0 | g_free (file->priv->etag); |
152 | |
|
153 | 0 | G_OBJECT_CLASS (g_local_file_output_stream_parent_class)->finalize (object); |
154 | 0 | } |
155 | | |
156 | | static void |
157 | | g_local_file_output_stream_class_init (GLocalFileOutputStreamClass *klass) |
158 | 0 | { |
159 | 0 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
160 | 0 | GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass); |
161 | 0 | GFileOutputStreamClass *file_stream_class = G_FILE_OUTPUT_STREAM_CLASS (klass); |
162 | |
|
163 | 0 | gobject_class->finalize = g_local_file_output_stream_finalize; |
164 | |
|
165 | 0 | stream_class->write_fn = g_local_file_output_stream_write; |
166 | 0 | #ifdef G_OS_UNIX |
167 | 0 | stream_class->writev_fn = g_local_file_output_stream_writev; |
168 | 0 | #endif |
169 | 0 | stream_class->close_fn = g_local_file_output_stream_close; |
170 | 0 | file_stream_class->query_info = g_local_file_output_stream_query_info; |
171 | 0 | file_stream_class->get_etag = g_local_file_output_stream_get_etag; |
172 | 0 | file_stream_class->tell = g_local_file_output_stream_tell; |
173 | 0 | file_stream_class->can_seek = g_local_file_output_stream_can_seek; |
174 | 0 | file_stream_class->seek = g_local_file_output_stream_seek; |
175 | 0 | file_stream_class->can_truncate = g_local_file_output_stream_can_truncate; |
176 | 0 | file_stream_class->truncate_fn = g_local_file_output_stream_truncate; |
177 | 0 | } |
178 | | |
179 | | #ifdef G_OS_UNIX |
180 | | static void |
181 | | g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface) |
182 | 0 | { |
183 | 0 | iface->get_fd = g_local_file_output_stream_get_fd; |
184 | 0 | } |
185 | | #endif |
186 | | |
187 | | static void |
188 | | g_local_file_output_stream_init (GLocalFileOutputStream *stream) |
189 | 0 | { |
190 | 0 | stream->priv = g_local_file_output_stream_get_instance_private (stream); |
191 | 0 | stream->priv->do_close = TRUE; |
192 | 0 | } |
193 | | |
194 | | static gssize |
195 | | g_local_file_output_stream_write (GOutputStream *stream, |
196 | | const void *buffer, |
197 | | gsize count, |
198 | | GCancellable *cancellable, |
199 | | GError **error) |
200 | 0 | { |
201 | 0 | GLocalFileOutputStream *file; |
202 | 0 | gssize res; |
203 | |
|
204 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
205 | |
|
206 | 0 | while (1) |
207 | 0 | { |
208 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
209 | 0 | return -1; |
210 | 0 | res = write (file->priv->fd, buffer, count); |
211 | 0 | if (res == -1) |
212 | 0 | { |
213 | 0 | int errsv = errno; |
214 | |
|
215 | 0 | if (errsv == EINTR) |
216 | 0 | continue; |
217 | | |
218 | 0 | g_set_error (error, G_IO_ERROR, |
219 | 0 | g_io_error_from_errno (errsv), |
220 | 0 | _("Error writing to file: %s"), |
221 | 0 | g_strerror (errsv)); |
222 | 0 | } |
223 | | |
224 | 0 | break; |
225 | 0 | } |
226 | | |
227 | 0 | return res; |
228 | 0 | } |
229 | | |
230 | | /* On Windows there is no equivalent API for files. The closest API to that is |
231 | | * WriteFileGather() but it is useless in general: it requires, among other |
232 | | * things, that each chunk is the size of a whole page and in memory aligned |
233 | | * to a page. We can't possibly guarantee that in GLib. |
234 | | */ |
235 | | #ifdef G_OS_UNIX |
236 | | /* Macro to check if struct iovec and GOutputVector have the same ABI */ |
237 | 0 | #define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \ |
238 | 0 | G_SIZEOF_MEMBER (struct iovec, iov_base) == G_SIZEOF_MEMBER (GOutputVector, buffer) && \ |
239 | 0 | G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \ |
240 | 0 | G_SIZEOF_MEMBER (struct iovec, iov_len) == G_SIZEOF_MEMBER (GOutputVector, size) && \ |
241 | 0 | G_STRUCT_OFFSET (struct iovec, iov_len) == G_STRUCT_OFFSET (GOutputVector, size)) |
242 | | |
243 | | static gboolean |
244 | | g_local_file_output_stream_writev (GOutputStream *stream, |
245 | | const GOutputVector *vectors, |
246 | | gsize n_vectors, |
247 | | gsize *bytes_written, |
248 | | GCancellable *cancellable, |
249 | | GError **error) |
250 | 0 | { |
251 | 0 | GLocalFileOutputStream *file; |
252 | 0 | gssize res; |
253 | 0 | struct iovec *iov; |
254 | |
|
255 | 0 | if (bytes_written) |
256 | 0 | *bytes_written = 0; |
257 | | |
258 | | /* Clamp the number of vectors if more given than we can write in one go. |
259 | | * The caller has to handle short writes anyway. |
260 | | */ |
261 | 0 | if (n_vectors > G_IOV_MAX) |
262 | 0 | n_vectors = G_IOV_MAX; |
263 | |
|
264 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
265 | |
|
266 | 0 | if (G_OUTPUT_VECTOR_IS_IOVEC) |
267 | 0 | { |
268 | | /* ABI is compatible */ |
269 | 0 | iov = (struct iovec *) vectors; |
270 | 0 | } |
271 | 0 | else |
272 | 0 | { |
273 | 0 | gsize i; |
274 | | |
275 | | /* ABI is incompatible */ |
276 | 0 | iov = g_newa (struct iovec, n_vectors); |
277 | 0 | for (i = 0; i < n_vectors; i++) |
278 | 0 | { |
279 | 0 | iov[i].iov_base = (void *)vectors[i].buffer; |
280 | 0 | iov[i].iov_len = vectors[i].size; |
281 | 0 | } |
282 | 0 | } |
283 | |
|
284 | 0 | while (1) |
285 | 0 | { |
286 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
287 | 0 | return FALSE; |
288 | 0 | res = writev (file->priv->fd, iov, n_vectors); |
289 | 0 | if (res == -1) |
290 | 0 | { |
291 | 0 | int errsv = errno; |
292 | |
|
293 | 0 | if (errsv == EINTR) |
294 | 0 | continue; |
295 | | |
296 | 0 | g_set_error (error, G_IO_ERROR, |
297 | 0 | g_io_error_from_errno (errsv), |
298 | 0 | _("Error writing to file: %s"), |
299 | 0 | g_strerror (errsv)); |
300 | 0 | } |
301 | 0 | else if (bytes_written) |
302 | 0 | { |
303 | 0 | *bytes_written = res; |
304 | 0 | } |
305 | | |
306 | 0 | break; |
307 | 0 | } |
308 | | |
309 | 0 | return res != -1; |
310 | 0 | } |
311 | | #endif |
312 | | |
313 | | void |
314 | | _g_local_file_output_stream_set_do_close (GLocalFileOutputStream *out, |
315 | | gboolean do_close) |
316 | 0 | { |
317 | 0 | out->priv->do_close = do_close; |
318 | 0 | } |
319 | | |
320 | | gboolean |
321 | | _g_local_file_output_stream_really_close (GLocalFileOutputStream *file, |
322 | | GCancellable *cancellable, |
323 | | GError **error) |
324 | 0 | { |
325 | 0 | GLocalFileStat final_stat; |
326 | |
|
327 | 0 | if (file->priv->sync_on_close && |
328 | 0 | g_fsync (file->priv->fd) != 0) |
329 | 0 | { |
330 | 0 | int errsv = errno; |
331 | | |
332 | 0 | g_set_error (error, G_IO_ERROR, |
333 | 0 | g_io_error_from_errno (errsv), |
334 | 0 | _("Error writing to file: %s"), |
335 | 0 | g_strerror (errsv)); |
336 | 0 | goto err_out; |
337 | 0 | } |
338 | | |
339 | | #ifdef G_OS_WIN32 |
340 | | |
341 | | /* Must close before renaming on Windows, so just do the close first |
342 | | * in all cases for now. |
343 | | */ |
344 | | if (GLIB_PRIVATE_CALL (g_win32_fstat) (file->priv->fd, &final_stat) == 0) |
345 | | file->priv->etag = _g_local_file_info_create_etag (&final_stat); |
346 | | |
347 | | if (!g_close (file->priv->fd, NULL)) |
348 | | { |
349 | | int errsv = errno; |
350 | | |
351 | | g_set_error (error, G_IO_ERROR, |
352 | | g_io_error_from_errno (errsv), |
353 | | _("Error closing file: %s"), |
354 | | g_strerror (errsv)); |
355 | | return FALSE; |
356 | | } |
357 | | |
358 | | #endif |
359 | | |
360 | 0 | if (file->priv->tmp_filename) |
361 | 0 | { |
362 | | /* We need to move the temp file to its final place, |
363 | | * and possibly create the backup file |
364 | | */ |
365 | |
|
366 | 0 | if (file->priv->backup_filename) |
367 | 0 | { |
368 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
369 | 0 | goto err_out; |
370 | | |
371 | 0 | #ifdef G_OS_UNIX |
372 | | /* create original -> backup link, the original is then renamed over */ |
373 | 0 | if (g_unlink (file->priv->backup_filename) != 0 && |
374 | 0 | errno != ENOENT) |
375 | 0 | { |
376 | 0 | int errsv = errno; |
377 | |
|
378 | 0 | g_set_error (error, G_IO_ERROR, |
379 | 0 | G_IO_ERROR_CANT_CREATE_BACKUP, |
380 | 0 | _("Error removing old backup link: %s"), |
381 | 0 | g_strerror (errsv)); |
382 | 0 | goto err_out; |
383 | 0 | } |
384 | | |
385 | 0 | if (link (file->priv->original_filename, file->priv->backup_filename) != 0) |
386 | 0 | { |
387 | | /* link failed or is not supported, try rename */ |
388 | 0 | if (g_rename (file->priv->original_filename, file->priv->backup_filename) != 0) |
389 | 0 | { |
390 | 0 | int errsv = errno; |
391 | |
|
392 | 0 | g_set_error (error, G_IO_ERROR, |
393 | 0 | G_IO_ERROR_CANT_CREATE_BACKUP, |
394 | 0 | _("Error creating backup copy: %s"), |
395 | 0 | g_strerror (errsv)); |
396 | 0 | goto err_out; |
397 | 0 | } |
398 | 0 | } |
399 | | #else |
400 | | /* If link not supported, just rename... */ |
401 | | if (g_rename (file->priv->original_filename, file->priv->backup_filename) != 0) |
402 | | { |
403 | | int errsv = errno; |
404 | | |
405 | | g_set_error (error, G_IO_ERROR, |
406 | | G_IO_ERROR_CANT_CREATE_BACKUP, |
407 | | _("Error creating backup copy: %s"), |
408 | | g_strerror (errsv)); |
409 | | goto err_out; |
410 | | } |
411 | | #endif |
412 | 0 | } |
413 | | |
414 | | |
415 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
416 | 0 | goto err_out; |
417 | | |
418 | | /* tmp -> original */ |
419 | 0 | if (g_rename (file->priv->tmp_filename, file->priv->original_filename) != 0) |
420 | 0 | { |
421 | 0 | int errsv = errno; |
422 | |
|
423 | 0 | g_set_error (error, G_IO_ERROR, |
424 | 0 | g_io_error_from_errno (errsv), |
425 | 0 | _("Error renaming temporary file: %s"), |
426 | 0 | g_strerror (errsv)); |
427 | 0 | goto err_out; |
428 | 0 | } |
429 | | |
430 | 0 | g_clear_pointer (&file->priv->tmp_filename, g_free); |
431 | 0 | } |
432 | | |
433 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
434 | 0 | goto err_out; |
435 | | |
436 | 0 | #ifndef G_OS_WIN32 /* Already did the fstat() and close() above on Win32 */ |
437 | | |
438 | 0 | if (g_local_file_fstat (file->priv->fd, G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, &final_stat) == 0) |
439 | 0 | file->priv->etag = _g_local_file_info_create_etag (&final_stat); |
440 | |
|
441 | 0 | if (!g_close (file->priv->fd, NULL)) |
442 | 0 | { |
443 | 0 | int errsv = errno; |
444 | | |
445 | 0 | g_set_error (error, G_IO_ERROR, |
446 | 0 | g_io_error_from_errno (errsv), |
447 | 0 | _("Error closing file: %s"), |
448 | 0 | g_strerror (errsv)); |
449 | 0 | goto err_out; |
450 | 0 | } |
451 | | |
452 | 0 | #endif |
453 | | |
454 | 0 | return TRUE; |
455 | 0 | err_out: |
456 | |
|
457 | 0 | #ifndef G_OS_WIN32 |
458 | | /* A simple try to close the fd in case we fail before the actual close */ |
459 | 0 | g_close (file->priv->fd, NULL); |
460 | 0 | #endif |
461 | 0 | if (file->priv->tmp_filename) |
462 | 0 | g_unlink (file->priv->tmp_filename); |
463 | |
|
464 | 0 | return FALSE; |
465 | 0 | } |
466 | | |
467 | | |
468 | | static gboolean |
469 | | g_local_file_output_stream_close (GOutputStream *stream, |
470 | | GCancellable *cancellable, |
471 | | GError **error) |
472 | 0 | { |
473 | 0 | GLocalFileOutputStream *file; |
474 | |
|
475 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
476 | |
|
477 | 0 | if (file->priv->do_close) |
478 | 0 | return _g_local_file_output_stream_really_close (file, |
479 | 0 | cancellable, |
480 | 0 | error); |
481 | 0 | return TRUE; |
482 | 0 | } |
483 | | |
484 | | static char * |
485 | | g_local_file_output_stream_get_etag (GFileOutputStream *stream) |
486 | 0 | { |
487 | 0 | GLocalFileOutputStream *file; |
488 | |
|
489 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
490 | | |
491 | 0 | return g_strdup (file->priv->etag); |
492 | 0 | } |
493 | | |
494 | | static goffset |
495 | | g_local_file_output_stream_tell (GFileOutputStream *stream) |
496 | 0 | { |
497 | 0 | GLocalFileOutputStream *file; |
498 | 0 | off_t pos; |
499 | |
|
500 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
501 | | |
502 | 0 | pos = lseek (file->priv->fd, 0, SEEK_CUR); |
503 | |
|
504 | 0 | if (pos == (off_t)-1) |
505 | 0 | return 0; |
506 | | |
507 | 0 | return pos; |
508 | 0 | } |
509 | | |
510 | | static gboolean |
511 | | g_local_file_output_stream_can_seek (GFileOutputStream *stream) |
512 | 0 | { |
513 | 0 | GLocalFileOutputStream *file; |
514 | 0 | off_t pos; |
515 | |
|
516 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
517 | | |
518 | 0 | pos = lseek (file->priv->fd, 0, SEEK_CUR); |
519 | |
|
520 | 0 | if (pos == (off_t)-1 && errno == ESPIPE) |
521 | 0 | return FALSE; |
522 | | |
523 | 0 | return TRUE; |
524 | 0 | } |
525 | | |
526 | | static int |
527 | | seek_type_to_lseek (GSeekType type) |
528 | 0 | { |
529 | 0 | switch (type) |
530 | 0 | { |
531 | 0 | default: |
532 | 0 | case G_SEEK_CUR: |
533 | 0 | return SEEK_CUR; |
534 | | |
535 | 0 | case G_SEEK_SET: |
536 | 0 | return SEEK_SET; |
537 | | |
538 | 0 | case G_SEEK_END: |
539 | 0 | return SEEK_END; |
540 | 0 | } |
541 | 0 | } |
542 | | |
543 | | static gboolean |
544 | | g_local_file_output_stream_seek (GFileOutputStream *stream, |
545 | | goffset offset, |
546 | | GSeekType type, |
547 | | GCancellable *cancellable, |
548 | | GError **error) |
549 | 0 | { |
550 | 0 | GLocalFileOutputStream *file; |
551 | 0 | off_t pos; |
552 | |
|
553 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
554 | |
|
555 | 0 | pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type)); |
556 | |
|
557 | 0 | if (pos == (off_t)-1) |
558 | 0 | { |
559 | 0 | int errsv = errno; |
560 | |
|
561 | 0 | g_set_error (error, G_IO_ERROR, |
562 | 0 | g_io_error_from_errno (errsv), |
563 | 0 | _("Error seeking in file: %s"), |
564 | 0 | g_strerror (errsv)); |
565 | 0 | return FALSE; |
566 | 0 | } |
567 | | |
568 | 0 | return TRUE; |
569 | 0 | } |
570 | | |
571 | | static gboolean |
572 | | g_local_file_output_stream_can_truncate (GFileOutputStream *stream) |
573 | 0 | { |
574 | | /* We can't truncate pipes and stuff where we can't seek */ |
575 | 0 | return g_local_file_output_stream_can_seek (stream); |
576 | 0 | } |
577 | | |
578 | | static gboolean |
579 | | g_local_file_output_stream_truncate (GFileOutputStream *stream, |
580 | | goffset size, |
581 | | GCancellable *cancellable, |
582 | | GError **error) |
583 | 0 | { |
584 | 0 | GLocalFileOutputStream *file; |
585 | 0 | int res; |
586 | |
|
587 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
588 | |
|
589 | 0 | restart: |
590 | | #ifdef G_OS_WIN32 |
591 | | res = g_win32_ftruncate (file->priv->fd, size); |
592 | | #else |
593 | 0 | res = ftruncate (file->priv->fd, size); |
594 | 0 | #endif |
595 | | |
596 | 0 | if (res == -1) |
597 | 0 | { |
598 | 0 | int errsv = errno; |
599 | |
|
600 | 0 | if (errsv == EINTR) |
601 | 0 | { |
602 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
603 | 0 | return FALSE; |
604 | 0 | goto restart; |
605 | 0 | } |
606 | | |
607 | 0 | g_set_error (error, G_IO_ERROR, |
608 | 0 | g_io_error_from_errno (errsv), |
609 | 0 | _("Error truncating file: %s"), |
610 | 0 | g_strerror (errsv)); |
611 | 0 | return FALSE; |
612 | 0 | } |
613 | | |
614 | 0 | return TRUE; |
615 | 0 | } |
616 | | |
617 | | |
618 | | static GFileInfo * |
619 | | g_local_file_output_stream_query_info (GFileOutputStream *stream, |
620 | | const char *attributes, |
621 | | GCancellable *cancellable, |
622 | | GError **error) |
623 | 0 | { |
624 | 0 | GLocalFileOutputStream *file; |
625 | |
|
626 | 0 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
627 | |
|
628 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
629 | 0 | return NULL; |
630 | | |
631 | 0 | return _g_local_file_info_get_from_fd (file->priv->fd, |
632 | 0 | attributes, |
633 | 0 | error); |
634 | 0 | } |
635 | | |
636 | | GFileOutputStream * |
637 | | _g_local_file_output_stream_new (int fd) |
638 | 0 | { |
639 | 0 | GLocalFileOutputStream *stream; |
640 | |
|
641 | 0 | stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL); |
642 | 0 | stream->priv->fd = fd; |
643 | 0 | return G_FILE_OUTPUT_STREAM (stream); |
644 | 0 | } |
645 | | |
646 | | static void |
647 | | set_error_from_open_errno (const char *filename, |
648 | | GError **error) |
649 | 0 | { |
650 | 0 | int errsv = errno; |
651 | | |
652 | 0 | if (errsv == EINVAL) |
653 | | /* This must be an invalid filename, on e.g. FAT */ |
654 | 0 | g_set_error_literal (error, G_IO_ERROR, |
655 | 0 | G_IO_ERROR_INVALID_FILENAME, |
656 | 0 | _("Invalid filename")); |
657 | 0 | else |
658 | 0 | { |
659 | 0 | char *display_name = g_filename_display_name (filename); |
660 | 0 | g_set_error (error, G_IO_ERROR, |
661 | 0 | g_io_error_from_errno (errsv), |
662 | 0 | _("Error opening file ā%sā: %s"), |
663 | 0 | display_name, g_strerror (errsv)); |
664 | 0 | g_free (display_name); |
665 | 0 | } |
666 | 0 | } |
667 | | |
668 | | static GFileOutputStream * |
669 | | output_stream_open (const char *filename, |
670 | | gint open_flags, |
671 | | guint mode, |
672 | | GCancellable *cancellable, |
673 | | GError **error) |
674 | 0 | { |
675 | 0 | GLocalFileOutputStream *stream; |
676 | 0 | gint fd; |
677 | |
|
678 | 0 | fd = g_open (filename, open_flags, mode); |
679 | 0 | if (fd == -1) |
680 | 0 | { |
681 | 0 | set_error_from_open_errno (filename, error); |
682 | 0 | return NULL; |
683 | 0 | } |
684 | | |
685 | 0 | stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL); |
686 | 0 | stream->priv->fd = fd; |
687 | 0 | return G_FILE_OUTPUT_STREAM (stream); |
688 | 0 | } |
689 | | |
690 | | GFileOutputStream * |
691 | | _g_local_file_output_stream_open (const char *filename, |
692 | | gboolean readable, |
693 | | GCancellable *cancellable, |
694 | | GError **error) |
695 | 0 | { |
696 | 0 | int open_flags; |
697 | |
|
698 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
699 | 0 | return NULL; |
700 | | |
701 | 0 | open_flags = O_BINARY; |
702 | 0 | if (readable) |
703 | 0 | open_flags |= O_RDWR; |
704 | 0 | else |
705 | 0 | open_flags |= O_WRONLY; |
706 | |
|
707 | 0 | return output_stream_open (filename, open_flags, 0666, cancellable, error); |
708 | 0 | } |
709 | | |
710 | | static gint |
711 | | mode_from_flags_or_info (GFileCreateFlags flags, |
712 | | GFileInfo *reference_info) |
713 | 0 | { |
714 | 0 | if (flags & G_FILE_CREATE_PRIVATE) |
715 | 0 | return 0600; |
716 | 0 | else if (reference_info && g_file_info_has_attribute (reference_info, "unix::mode")) |
717 | 0 | return g_file_info_get_attribute_uint32 (reference_info, "unix::mode") & (~S_IFMT); |
718 | 0 | else |
719 | 0 | return 0666; |
720 | 0 | } |
721 | | |
722 | | GFileOutputStream * |
723 | | _g_local_file_output_stream_create (const char *filename, |
724 | | gboolean readable, |
725 | | GFileCreateFlags flags, |
726 | | GFileInfo *reference_info, |
727 | | GCancellable *cancellable, |
728 | | GError **error) |
729 | 0 | { |
730 | 0 | int mode; |
731 | 0 | int open_flags; |
732 | |
|
733 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
734 | 0 | return NULL; |
735 | | |
736 | 0 | mode = mode_from_flags_or_info (flags, reference_info); |
737 | |
|
738 | 0 | open_flags = O_CREAT | O_EXCL | O_BINARY; |
739 | 0 | if (readable) |
740 | 0 | open_flags |= O_RDWR; |
741 | 0 | else |
742 | 0 | open_flags |= O_WRONLY; |
743 | |
|
744 | 0 | return output_stream_open (filename, open_flags, mode, cancellable, error); |
745 | 0 | } |
746 | | |
747 | | GFileOutputStream * |
748 | | _g_local_file_output_stream_append (const char *filename, |
749 | | GFileCreateFlags flags, |
750 | | GCancellable *cancellable, |
751 | | GError **error) |
752 | 0 | { |
753 | 0 | int mode; |
754 | |
|
755 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
756 | 0 | return NULL; |
757 | | |
758 | 0 | if (flags & G_FILE_CREATE_PRIVATE) |
759 | 0 | mode = 0600; |
760 | 0 | else |
761 | 0 | mode = 0666; |
762 | |
|
763 | 0 | return output_stream_open (filename, O_CREAT | O_APPEND | O_WRONLY | O_BINARY, mode, |
764 | 0 | cancellable, error); |
765 | 0 | } |
766 | | |
767 | | static char * |
768 | | create_backup_filename (const char *filename) |
769 | 0 | { |
770 | 0 | return g_strconcat (filename, BACKUP_EXTENSION, NULL); |
771 | 0 | } |
772 | | |
773 | 0 | #define BUFSIZE 8192 /* size of normal write buffer */ |
774 | | |
775 | | static gboolean |
776 | | copy_file_data (gint sfd, |
777 | | gint dfd, |
778 | | GError **error) |
779 | 0 | { |
780 | 0 | gboolean ret = TRUE; |
781 | 0 | gpointer buffer; |
782 | 0 | const gchar *write_buffer; |
783 | 0 | gssize bytes_read; |
784 | 0 | gssize bytes_to_write; |
785 | 0 | gssize bytes_written; |
786 | | |
787 | 0 | buffer = g_malloc (BUFSIZE); |
788 | | |
789 | 0 | do |
790 | 0 | { |
791 | 0 | bytes_read = read (sfd, buffer, BUFSIZE); |
792 | 0 | if (bytes_read == -1) |
793 | 0 | { |
794 | 0 | int errsv = errno; |
795 | |
|
796 | 0 | if (errsv == EINTR) |
797 | 0 | continue; |
798 | | |
799 | 0 | g_set_error (error, G_IO_ERROR, |
800 | 0 | g_io_error_from_errno (errsv), |
801 | 0 | _("Error reading from file: %s"), |
802 | 0 | g_strerror (errsv)); |
803 | 0 | ret = FALSE; |
804 | 0 | break; |
805 | 0 | } |
806 | | |
807 | 0 | bytes_to_write = bytes_read; |
808 | 0 | write_buffer = buffer; |
809 | | |
810 | 0 | do |
811 | 0 | { |
812 | 0 | bytes_written = write (dfd, write_buffer, bytes_to_write); |
813 | 0 | if (bytes_written == -1) |
814 | 0 | { |
815 | 0 | int errsv = errno; |
816 | |
|
817 | 0 | if (errsv == EINTR) |
818 | 0 | continue; |
819 | | |
820 | 0 | g_set_error (error, G_IO_ERROR, |
821 | 0 | g_io_error_from_errno (errsv), |
822 | 0 | _("Error writing to file: %s"), |
823 | 0 | g_strerror (errsv)); |
824 | 0 | ret = FALSE; |
825 | 0 | break; |
826 | 0 | } |
827 | | |
828 | 0 | bytes_to_write -= bytes_written; |
829 | 0 | write_buffer += bytes_written; |
830 | 0 | } |
831 | 0 | while (bytes_to_write > 0); |
832 | | |
833 | 0 | } while ((bytes_read != 0) && (ret == TRUE)); |
834 | | |
835 | 0 | g_free (buffer); |
836 | | |
837 | 0 | return ret; |
838 | 0 | } |
839 | | |
840 | | static int |
841 | | handle_overwrite_open (const char *filename, |
842 | | gboolean readable, |
843 | | const char *etag, |
844 | | gboolean create_backup, |
845 | | char **temp_filename, |
846 | | GFileCreateFlags flags, |
847 | | GFileInfo *reference_info, |
848 | | GCancellable *cancellable, |
849 | | GError **error) |
850 | 0 | { |
851 | 0 | int fd = -1; |
852 | 0 | GLocalFileStat original_stat; |
853 | 0 | char *current_etag; |
854 | 0 | gboolean is_symlink; |
855 | 0 | int open_flags; |
856 | 0 | int res; |
857 | 0 | int mode; |
858 | 0 | int errsv; |
859 | 0 | gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION); |
860 | |
|
861 | 0 | mode = mode_from_flags_or_info (flags, reference_info); |
862 | | |
863 | | /* We only need read access to the original file if we are creating a backup. |
864 | | * We also add O_CREAT to avoid a race if the file was just removed */ |
865 | 0 | if (create_backup || readable) |
866 | 0 | open_flags = O_RDWR | O_CREAT | O_BINARY; |
867 | 0 | else |
868 | 0 | open_flags = O_WRONLY | O_CREAT | O_BINARY; |
869 | | |
870 | | /* Some systems have O_NOFOLLOW, which lets us avoid some races |
871 | | * when finding out if the file we opened was a symlink */ |
872 | 0 | #ifdef O_NOFOLLOW |
873 | 0 | is_symlink = FALSE; |
874 | 0 | fd = g_open (filename, open_flags | O_NOFOLLOW, mode); |
875 | 0 | errsv = errno; |
876 | | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
877 | | if (fd == -1 && errsv == EMLINK) |
878 | | #elif defined(__NetBSD__) |
879 | | if (fd == -1 && errsv == EFTYPE) |
880 | | #else |
881 | 0 | if (fd == -1 && errsv == ELOOP) |
882 | 0 | #endif |
883 | 0 | { |
884 | | /* Could be a symlink, or it could be a regular ELOOP error, |
885 | | * but then the next open will fail too. */ |
886 | 0 | is_symlink = TRUE; |
887 | 0 | if (!replace_destination_set) |
888 | 0 | fd = g_open (filename, open_flags, mode); |
889 | 0 | } |
890 | | #else /* if !O_NOFOLLOW */ |
891 | | /* This is racy, but we do it as soon as possible to minimize the race */ |
892 | | is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK); |
893 | | |
894 | | if (!is_symlink || !replace_destination_set) |
895 | | { |
896 | | fd = g_open (filename, open_flags, mode); |
897 | | errsv = errno; |
898 | | } |
899 | | #endif |
900 | |
|
901 | 0 | if (fd == -1 && |
902 | 0 | (!is_symlink || !replace_destination_set)) |
903 | 0 | { |
904 | 0 | char *display_name = g_filename_display_name (filename); |
905 | 0 | g_set_error (error, G_IO_ERROR, |
906 | 0 | g_io_error_from_errno (errsv), |
907 | 0 | _("Error opening file ā%sā: %s"), |
908 | 0 | display_name, g_strerror (errsv)); |
909 | 0 | g_free (display_name); |
910 | 0 | return -1; |
911 | 0 | } |
912 | | |
913 | 0 | if (!is_symlink) |
914 | 0 | { |
915 | 0 | res = g_local_file_fstat (fd, |
916 | 0 | G_LOCAL_FILE_STAT_FIELD_TYPE | |
917 | 0 | G_LOCAL_FILE_STAT_FIELD_MODE | |
918 | 0 | G_LOCAL_FILE_STAT_FIELD_UID | |
919 | 0 | G_LOCAL_FILE_STAT_FIELD_GID | |
920 | 0 | G_LOCAL_FILE_STAT_FIELD_MTIME | |
921 | 0 | G_LOCAL_FILE_STAT_FIELD_NLINK, |
922 | 0 | G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat); |
923 | 0 | errsv = errno; |
924 | 0 | } |
925 | 0 | else |
926 | 0 | { |
927 | 0 | res = g_local_file_lstat (filename, |
928 | 0 | G_LOCAL_FILE_STAT_FIELD_TYPE | |
929 | 0 | G_LOCAL_FILE_STAT_FIELD_MODE | |
930 | 0 | G_LOCAL_FILE_STAT_FIELD_UID | |
931 | 0 | G_LOCAL_FILE_STAT_FIELD_GID | |
932 | 0 | G_LOCAL_FILE_STAT_FIELD_MTIME | |
933 | 0 | G_LOCAL_FILE_STAT_FIELD_NLINK, |
934 | 0 | G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat); |
935 | 0 | errsv = errno; |
936 | 0 | } |
937 | |
|
938 | 0 | if (res != 0) |
939 | 0 | { |
940 | 0 | char *display_name = g_filename_display_name (filename); |
941 | 0 | g_set_error (error, G_IO_ERROR, |
942 | 0 | g_io_error_from_errno (errsv), |
943 | 0 | _("Error when getting information for file ā%sā: %s"), |
944 | 0 | display_name, g_strerror (errsv)); |
945 | 0 | g_free (display_name); |
946 | 0 | goto error; |
947 | 0 | } |
948 | | |
949 | | /* not a regular file */ |
950 | 0 | if (!S_ISREG (_g_stat_mode (&original_stat))) |
951 | 0 | { |
952 | 0 | if (S_ISDIR (_g_stat_mode (&original_stat))) |
953 | 0 | { |
954 | 0 | g_set_error_literal (error, |
955 | 0 | G_IO_ERROR, |
956 | 0 | G_IO_ERROR_IS_DIRECTORY, |
957 | 0 | _("Target file is a directory")); |
958 | 0 | goto error; |
959 | 0 | } |
960 | 0 | else if (!is_symlink || |
961 | 0 | #ifdef S_ISLNK |
962 | 0 | !S_ISLNK (_g_stat_mode (&original_stat)) |
963 | | #else |
964 | | FALSE |
965 | | #endif |
966 | 0 | ) |
967 | 0 | { |
968 | 0 | g_set_error_literal (error, |
969 | 0 | G_IO_ERROR, |
970 | 0 | G_IO_ERROR_NOT_REGULAR_FILE, |
971 | 0 | _("Target file is not a regular file")); |
972 | 0 | goto error; |
973 | 0 | } |
974 | 0 | } |
975 | | |
976 | 0 | if (etag != NULL) |
977 | 0 | { |
978 | 0 | GLocalFileStat etag_stat; |
979 | 0 | GLocalFileStat *etag_stat_pointer; |
980 | | |
981 | | /* The ETag is calculated on the details of the target file, for symlinks, |
982 | | * so we might need to stat() again. */ |
983 | 0 | if (is_symlink) |
984 | 0 | { |
985 | 0 | res = g_local_file_stat (filename, |
986 | 0 | G_LOCAL_FILE_STAT_FIELD_MTIME, |
987 | 0 | G_LOCAL_FILE_STAT_FIELD_ALL, &etag_stat); |
988 | 0 | errsv = errno; |
989 | |
|
990 | 0 | if (res != 0) |
991 | 0 | { |
992 | 0 | char *display_name = g_filename_display_name (filename); |
993 | 0 | g_set_error (error, G_IO_ERROR, |
994 | 0 | g_io_error_from_errno (errsv), |
995 | 0 | _("Error when getting information for file ā%sā: %s"), |
996 | 0 | display_name, g_strerror (errsv)); |
997 | 0 | g_free (display_name); |
998 | 0 | goto error; |
999 | 0 | } |
1000 | | |
1001 | 0 | etag_stat_pointer = &etag_stat; |
1002 | 0 | } |
1003 | 0 | else |
1004 | 0 | etag_stat_pointer = &original_stat; |
1005 | | |
1006 | | /* Compare the ETags */ |
1007 | 0 | current_etag = _g_local_file_info_create_etag (etag_stat_pointer); |
1008 | 0 | if (strcmp (etag, current_etag) != 0) |
1009 | 0 | { |
1010 | 0 | g_set_error_literal (error, |
1011 | 0 | G_IO_ERROR, |
1012 | 0 | G_IO_ERROR_WRONG_ETAG, |
1013 | 0 | _("The file was externally modified")); |
1014 | 0 | g_free (current_etag); |
1015 | 0 | goto error; |
1016 | 0 | } |
1017 | 0 | g_free (current_etag); |
1018 | 0 | } |
1019 | | |
1020 | | /* We use two backup strategies. |
1021 | | * The first one (which is faster) consist in saving to a |
1022 | | * tmp file then rename the original file to the backup and the |
1023 | | * tmp file to the original name. This is fast but doesn't work |
1024 | | * when the file is a link (hard or symbolic) or when we can't |
1025 | | * write to the current dir or can't set the permissions on the |
1026 | | * new file. |
1027 | | * The second strategy consist simply in copying the old file |
1028 | | * to a backup file and rewrite the contents of the file. |
1029 | | */ |
1030 | | |
1031 | 0 | if (replace_destination_set || |
1032 | 0 | (!(_g_stat_nlink (&original_stat) > 1) && !is_symlink)) |
1033 | 0 | { |
1034 | 0 | char *dirname, *tmp_filename; |
1035 | 0 | int tmpfd; |
1036 | | |
1037 | 0 | dirname = g_path_get_dirname (filename); |
1038 | 0 | tmp_filename = g_build_filename (dirname, ".goutputstream-XXXXXX", NULL); |
1039 | 0 | g_free (dirname); |
1040 | |
|
1041 | 0 | tmpfd = g_mkstemp_full (tmp_filename, (readable ? O_RDWR : O_WRONLY) | O_BINARY, mode); |
1042 | 0 | if (tmpfd == -1) |
1043 | 0 | { |
1044 | 0 | g_free (tmp_filename); |
1045 | 0 | goto fallback_strategy; |
1046 | 0 | } |
1047 | | |
1048 | | /* try to keep permissions (unless replacing) */ |
1049 | | |
1050 | 0 | if (!replace_destination_set && |
1051 | 0 | ( |
1052 | 0 | #ifdef HAVE_FCHOWN |
1053 | 0 | fchown (tmpfd, _g_stat_uid (&original_stat), _g_stat_gid (&original_stat)) == -1 || |
1054 | 0 | #endif |
1055 | 0 | #ifdef HAVE_FCHMOD |
1056 | 0 | fchmod (tmpfd, _g_stat_mode (&original_stat) & ~S_IFMT) == -1 || |
1057 | 0 | #endif |
1058 | 0 | 0 |
1059 | 0 | ) |
1060 | 0 | ) |
1061 | 0 | { |
1062 | 0 | GLocalFileStat tmp_statbuf; |
1063 | 0 | int tres; |
1064 | |
|
1065 | 0 | tres = g_local_file_fstat (tmpfd, |
1066 | 0 | G_LOCAL_FILE_STAT_FIELD_TYPE | |
1067 | 0 | G_LOCAL_FILE_STAT_FIELD_MODE | |
1068 | 0 | G_LOCAL_FILE_STAT_FIELD_UID | |
1069 | 0 | G_LOCAL_FILE_STAT_FIELD_GID, |
1070 | 0 | G_LOCAL_FILE_STAT_FIELD_ALL, &tmp_statbuf); |
1071 | | |
1072 | | /* Check that we really needed to change something */ |
1073 | 0 | if (tres != 0 || |
1074 | 0 | _g_stat_uid (&original_stat) != _g_stat_uid (&tmp_statbuf) || |
1075 | 0 | _g_stat_gid (&original_stat) != _g_stat_gid (&tmp_statbuf) || |
1076 | 0 | _g_stat_mode (&original_stat) != _g_stat_mode (&tmp_statbuf)) |
1077 | 0 | { |
1078 | 0 | g_close (tmpfd, NULL); |
1079 | 0 | g_unlink (tmp_filename); |
1080 | 0 | g_free (tmp_filename); |
1081 | 0 | goto fallback_strategy; |
1082 | 0 | } |
1083 | 0 | } |
1084 | | |
1085 | 0 | if (fd >= 0) |
1086 | 0 | g_close (fd, NULL); |
1087 | 0 | *temp_filename = tmp_filename; |
1088 | 0 | return tmpfd; |
1089 | 0 | } |
1090 | | |
1091 | 0 | fallback_strategy: |
1092 | |
|
1093 | 0 | if (create_backup) |
1094 | 0 | { |
1095 | 0 | #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) |
1096 | 0 | GLocalFileStat tmp_statbuf; |
1097 | 0 | #endif |
1098 | 0 | char *backup_filename; |
1099 | 0 | int bfd; |
1100 | | |
1101 | 0 | backup_filename = create_backup_filename (filename); |
1102 | |
|
1103 | 0 | if (g_unlink (backup_filename) == -1 && errno != ENOENT) |
1104 | 0 | { |
1105 | 0 | g_set_error_literal (error, |
1106 | 0 | G_IO_ERROR, |
1107 | 0 | G_IO_ERROR_CANT_CREATE_BACKUP, |
1108 | 0 | _("Backup file creation failed")); |
1109 | 0 | g_free (backup_filename); |
1110 | 0 | goto error; |
1111 | 0 | } |
1112 | | |
1113 | 0 | bfd = g_open (backup_filename, |
1114 | 0 | O_WRONLY | O_CREAT | O_EXCL | O_BINARY, |
1115 | 0 | _g_stat_mode (&original_stat) & 0777); |
1116 | |
|
1117 | 0 | if (bfd == -1) |
1118 | 0 | { |
1119 | 0 | g_set_error_literal (error, |
1120 | 0 | G_IO_ERROR, |
1121 | 0 | G_IO_ERROR_CANT_CREATE_BACKUP, |
1122 | 0 | _("Backup file creation failed")); |
1123 | 0 | g_free (backup_filename); |
1124 | 0 | goto error; |
1125 | 0 | } |
1126 | | |
1127 | | /* If needed, Try to set the group of the backup same as the |
1128 | | * original file. If this fails, set the protection |
1129 | | * bits for the group same as the protection bits for |
1130 | | * others. */ |
1131 | 0 | #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) |
1132 | 0 | if (g_local_file_fstat (bfd, G_LOCAL_FILE_STAT_FIELD_GID, G_LOCAL_FILE_STAT_FIELD_ALL, &tmp_statbuf) != 0) |
1133 | 0 | { |
1134 | 0 | g_set_error_literal (error, |
1135 | 0 | G_IO_ERROR, |
1136 | 0 | G_IO_ERROR_CANT_CREATE_BACKUP, |
1137 | 0 | _("Backup file creation failed")); |
1138 | 0 | g_unlink (backup_filename); |
1139 | 0 | g_close (bfd, NULL); |
1140 | 0 | g_free (backup_filename); |
1141 | 0 | goto error; |
1142 | 0 | } |
1143 | | |
1144 | 0 | if ((_g_stat_gid (&original_stat) != _g_stat_gid (&tmp_statbuf)) && |
1145 | 0 | fchown (bfd, (uid_t) -1, _g_stat_gid (&original_stat)) != 0) |
1146 | 0 | { |
1147 | 0 | if (fchmod (bfd, |
1148 | 0 | (_g_stat_mode (&original_stat) & 0707) | |
1149 | 0 | ((_g_stat_mode (&original_stat) & 07) << 3)) != 0) |
1150 | 0 | { |
1151 | 0 | g_set_error_literal (error, |
1152 | 0 | G_IO_ERROR, |
1153 | 0 | G_IO_ERROR_CANT_CREATE_BACKUP, |
1154 | 0 | _("Backup file creation failed")); |
1155 | 0 | g_unlink (backup_filename); |
1156 | 0 | g_close (bfd, NULL); |
1157 | 0 | g_free (backup_filename); |
1158 | 0 | goto error; |
1159 | 0 | } |
1160 | 0 | } |
1161 | 0 | #endif |
1162 | | |
1163 | 0 | if (!copy_file_data (fd, bfd, NULL)) |
1164 | 0 | { |
1165 | 0 | g_set_error_literal (error, |
1166 | 0 | G_IO_ERROR, |
1167 | 0 | G_IO_ERROR_CANT_CREATE_BACKUP, |
1168 | 0 | _("Backup file creation failed")); |
1169 | 0 | g_unlink (backup_filename); |
1170 | 0 | g_close (bfd, NULL); |
1171 | 0 | g_free (backup_filename); |
1172 | | |
1173 | 0 | goto error; |
1174 | 0 | } |
1175 | | |
1176 | 0 | g_close (bfd, NULL); |
1177 | 0 | g_free (backup_filename); |
1178 | | |
1179 | | /* Seek back to the start of the file after the backup copy */ |
1180 | 0 | if (lseek (fd, 0, SEEK_SET) == -1) |
1181 | 0 | { |
1182 | 0 | int errsv = errno; |
1183 | |
|
1184 | 0 | g_set_error (error, G_IO_ERROR, |
1185 | 0 | g_io_error_from_errno (errsv), |
1186 | 0 | _("Error seeking in file: %s"), |
1187 | 0 | g_strerror (errsv)); |
1188 | 0 | goto error; |
1189 | 0 | } |
1190 | 0 | } |
1191 | | |
1192 | 0 | if (replace_destination_set) |
1193 | 0 | { |
1194 | 0 | g_close (fd, NULL); |
1195 | | |
1196 | 0 | if (g_unlink (filename) != 0) |
1197 | 0 | { |
1198 | 0 | int errsv = errno; |
1199 | | |
1200 | 0 | g_set_error (error, G_IO_ERROR, |
1201 | 0 | g_io_error_from_errno (errsv), |
1202 | 0 | _("Error removing old file: %s"), |
1203 | 0 | g_strerror (errsv)); |
1204 | 0 | goto error; |
1205 | 0 | } |
1206 | | |
1207 | 0 | if (readable) |
1208 | 0 | open_flags = O_RDWR | O_CREAT | O_BINARY; |
1209 | 0 | else |
1210 | 0 | open_flags = O_WRONLY | O_CREAT | O_BINARY; |
1211 | 0 | fd = g_open (filename, open_flags, mode); |
1212 | 0 | if (fd == -1) |
1213 | 0 | { |
1214 | 0 | int errsv = errno; |
1215 | 0 | char *display_name = g_filename_display_name (filename); |
1216 | 0 | g_set_error (error, G_IO_ERROR, |
1217 | 0 | g_io_error_from_errno (errsv), |
1218 | 0 | _("Error opening file ā%sā: %s"), |
1219 | 0 | display_name, g_strerror (errsv)); |
1220 | 0 | g_free (display_name); |
1221 | 0 | goto error; |
1222 | 0 | } |
1223 | 0 | } |
1224 | 0 | else |
1225 | 0 | { |
1226 | | /* Truncate the file at the start */ |
1227 | | #ifdef G_OS_WIN32 |
1228 | | if (g_win32_ftruncate (fd, 0) == -1) |
1229 | | #else |
1230 | 0 | if (ftruncate (fd, 0) == -1) |
1231 | 0 | #endif |
1232 | 0 | { |
1233 | 0 | int errsv = errno; |
1234 | | |
1235 | 0 | g_set_error (error, G_IO_ERROR, |
1236 | 0 | g_io_error_from_errno (errsv), |
1237 | 0 | _("Error truncating file: %s"), |
1238 | 0 | g_strerror (errsv)); |
1239 | 0 | goto error; |
1240 | 0 | } |
1241 | 0 | } |
1242 | | |
1243 | 0 | return fd; |
1244 | | |
1245 | 0 | error: |
1246 | 0 | if (fd >= 0) |
1247 | 0 | g_close (fd, NULL); |
1248 | |
|
1249 | 0 | return -1; |
1250 | 0 | } |
1251 | | |
1252 | | GFileOutputStream * |
1253 | | _g_local_file_output_stream_replace (const char *filename, |
1254 | | gboolean readable, |
1255 | | const char *etag, |
1256 | | gboolean create_backup, |
1257 | | GFileCreateFlags flags, |
1258 | | GFileInfo *reference_info, |
1259 | | GCancellable *cancellable, |
1260 | | GError **error) |
1261 | 0 | { |
1262 | 0 | GLocalFileOutputStream *stream; |
1263 | 0 | int mode; |
1264 | 0 | int fd; |
1265 | 0 | char *temp_file; |
1266 | 0 | gboolean sync_on_close; |
1267 | 0 | int open_flags; |
1268 | |
|
1269 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
1270 | 0 | return NULL; |
1271 | | |
1272 | 0 | temp_file = NULL; |
1273 | |
|
1274 | 0 | mode = mode_from_flags_or_info (flags, reference_info); |
1275 | 0 | sync_on_close = FALSE; |
1276 | | |
1277 | | /* If the file doesn't exist, create it */ |
1278 | 0 | open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC; |
1279 | 0 | if (readable) |
1280 | 0 | open_flags |= O_RDWR; |
1281 | 0 | else |
1282 | 0 | open_flags |= O_WRONLY; |
1283 | 0 | fd = g_open (filename, open_flags, mode); |
1284 | |
|
1285 | 0 | if (fd == -1 && errno == EEXIST) |
1286 | 0 | { |
1287 | | /* The file already exists */ |
1288 | 0 | fd = handle_overwrite_open (filename, readable, etag, |
1289 | 0 | create_backup, &temp_file, |
1290 | 0 | flags, reference_info, |
1291 | 0 | cancellable, error); |
1292 | 0 | if (fd == -1) |
1293 | 0 | return NULL; |
1294 | | |
1295 | | /* If the final destination exists, we want to sync the newly written |
1296 | | * file to ensure the data is on disk when we rename over the destination. |
1297 | | * otherwise if we get a system crash we can lose both the new and the |
1298 | | * old file on some filesystems. (I.E. those that don't guarantee the |
1299 | | * data is written to the disk before the metadata.) |
1300 | | */ |
1301 | 0 | sync_on_close = TRUE; |
1302 | 0 | } |
1303 | 0 | else if (fd == -1) |
1304 | 0 | { |
1305 | 0 | set_error_from_open_errno (filename, error); |
1306 | 0 | return NULL; |
1307 | 0 | } |
1308 | | #if !defined(HAVE_O_CLOEXEC) && defined(F_SETFD) |
1309 | | else |
1310 | | fcntl (fd, F_SETFD, FD_CLOEXEC); |
1311 | | #endif |
1312 | | |
1313 | 0 | stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL); |
1314 | 0 | stream->priv->fd = fd; |
1315 | 0 | stream->priv->sync_on_close = sync_on_close; |
1316 | 0 | stream->priv->tmp_filename = temp_file; |
1317 | 0 | if (create_backup) |
1318 | 0 | stream->priv->backup_filename = create_backup_filename (filename); |
1319 | 0 | stream->priv->original_filename = g_strdup (filename); |
1320 | | |
1321 | 0 | return G_FILE_OUTPUT_STREAM (stream); |
1322 | 0 | } |
1323 | | |
1324 | | gint |
1325 | | _g_local_file_output_stream_get_fd (GLocalFileOutputStream *stream) |
1326 | 0 | { |
1327 | 0 | g_return_val_if_fail (G_IS_LOCAL_FILE_OUTPUT_STREAM (stream), -1); |
1328 | 0 | return stream->priv->fd; |
1329 | 0 | } |
1330 | | |
1331 | | #ifdef G_OS_UNIX |
1332 | | static int |
1333 | | g_local_file_output_stream_get_fd (GFileDescriptorBased *fd_based) |
1334 | 0 | { |
1335 | 0 | GLocalFileOutputStream *stream = G_LOCAL_FILE_OUTPUT_STREAM (fd_based); |
1336 | 0 | return _g_local_file_output_stream_get_fd (stream); |
1337 | 0 | } |
1338 | | #endif |