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