Branch data Line data Source code
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 : : #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 : 67488 : 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 : : #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 : 288 : g_local_file_output_stream_finalize (GObject *object)
145 : : {
146 : : GLocalFileOutputStream *file;
147 : :
148 : 288 : file = G_LOCAL_FILE_OUTPUT_STREAM (object);
149 : :
150 : 288 : g_free (file->priv->tmp_filename);
151 : 288 : g_free (file->priv->original_filename);
152 : 288 : g_free (file->priv->backup_filename);
153 : 288 : g_free (file->priv->etag);
154 : :
155 : 288 : G_OBJECT_CLASS (g_local_file_output_stream_parent_class)->finalize (object);
156 : 288 : }
157 : :
158 : : static void
159 : 12 : g_local_file_output_stream_class_init (GLocalFileOutputStreamClass *klass)
160 : : {
161 : 12 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
162 : 12 : GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
163 : 12 : GFileOutputStreamClass *file_stream_class = G_FILE_OUTPUT_STREAM_CLASS (klass);
164 : :
165 : 12 : gobject_class->finalize = g_local_file_output_stream_finalize;
166 : :
167 : 12 : stream_class->write_fn = g_local_file_output_stream_write;
168 : : #ifdef G_OS_UNIX
169 : 12 : stream_class->writev_fn = g_local_file_output_stream_writev;
170 : : #endif
171 : 12 : stream_class->close_fn = g_local_file_output_stream_close;
172 : 12 : file_stream_class->query_info = g_local_file_output_stream_query_info;
173 : 12 : file_stream_class->get_etag = g_local_file_output_stream_get_etag;
174 : 12 : file_stream_class->tell = g_local_file_output_stream_tell;
175 : 12 : file_stream_class->can_seek = g_local_file_output_stream_can_seek;
176 : 12 : file_stream_class->seek = g_local_file_output_stream_seek;
177 : 12 : file_stream_class->can_truncate = g_local_file_output_stream_can_truncate;
178 : 12 : file_stream_class->truncate_fn = g_local_file_output_stream_truncate;
179 : 12 : }
180 : :
181 : : #ifdef G_OS_UNIX
182 : : static void
183 : 12 : g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
184 : : {
185 : 12 : iface->get_fd = g_local_file_output_stream_get_fd;
186 : 12 : }
187 : : #endif
188 : :
189 : : static void
190 : 289 : g_local_file_output_stream_init (GLocalFileOutputStream *stream)
191 : : {
192 : 289 : stream->priv = g_local_file_output_stream_get_instance_private (stream);
193 : 289 : stream->priv->do_close = TRUE;
194 : 289 : }
195 : :
196 : : static gssize
197 : 65777 : g_local_file_output_stream_write (GOutputStream *stream,
198 : : const void *buffer,
199 : : gsize count,
200 : : GCancellable *cancellable,
201 : : GError **error)
202 : : {
203 : : GLocalFileOutputStream *file;
204 : : gssize res;
205 : :
206 : 65777 : file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
207 : :
208 : : while (1)
209 : : {
210 : 65777 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
211 : 0 : return -1;
212 : 65777 : res = write (file->priv->fd, buffer, count);
213 : 65777 : if (res == -1)
214 : : {
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 : : _("Error writing to file: %s"),
223 : : g_strerror (errsv));
224 : : }
225 : :
226 : 65777 : break;
227 : : }
228 : :
229 : 65777 : return res;
230 : : }
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 : : #define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \
240 : : G_SIZEOF_MEMBER (struct iovec, iov_base) == G_SIZEOF_MEMBER (GOutputVector, buffer) && \
241 : : G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \
242 : : G_SIZEOF_MEMBER (struct iovec, iov_len) == G_SIZEOF_MEMBER (GOutputVector, size) && \
243 : : G_STRUCT_OFFSET (struct iovec, iov_len) == G_STRUCT_OFFSET (GOutputVector, size))
244 : :
245 : : static gboolean
246 : 5 : 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 : : {
253 : : GLocalFileOutputStream *file;
254 : : gssize res;
255 : : struct iovec *iov;
256 : :
257 : 5 : if (bytes_written)
258 : 5 : *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 : 5 : if (n_vectors > G_IOV_MAX)
264 : 0 : n_vectors = G_IOV_MAX;
265 : :
266 : 5 : file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
267 : :
268 : : if (G_OUTPUT_VECTOR_IS_IOVEC)
269 : : {
270 : : /* ABI is compatible */
271 : 5 : iov = (struct iovec *) vectors;
272 : : }
273 : : else
274 : : {
275 : : gsize i;
276 : :
277 : : /* ABI is incompatible */
278 : : iov = g_newa (struct iovec, n_vectors);
279 : : for (i = 0; i < n_vectors; i++)
280 : : {
281 : : iov[i].iov_base = (void *)vectors[i].buffer;
282 : : iov[i].iov_len = vectors[i].size;
283 : : }
284 : : }
285 : :
286 : : while (1)
287 : : {
288 : 5 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
289 : 1 : return FALSE;
290 : 4 : res = writev (file->priv->fd, iov, n_vectors);
291 : 4 : if (res == -1)
292 : : {
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 : : _("Error writing to file: %s"),
301 : : g_strerror (errsv));
302 : : }
303 : 4 : else if (bytes_written)
304 : : {
305 : 4 : *bytes_written = res;
306 : : }
307 : :
308 : 4 : break;
309 : : }
310 : :
311 : 4 : return res != -1;
312 : : }
313 : : #endif
314 : :
315 : : void
316 : 108 : _g_local_file_output_stream_set_do_close (GLocalFileOutputStream *out,
317 : : gboolean do_close)
318 : : {
319 : 108 : out->priv->do_close = do_close;
320 : 108 : }
321 : :
322 : : gboolean
323 : 289 : _g_local_file_output_stream_really_close (GLocalFileOutputStream *file,
324 : : GCancellable *cancellable,
325 : : GError **error)
326 : : {
327 : : GLocalFileStat final_stat;
328 : :
329 : 385 : if (file->priv->sync_on_close &&
330 : 96 : g_fsync (file->priv->fd) != 0)
331 : : {
332 : 0 : int errsv = errno;
333 : :
334 : 0 : g_set_error (error, G_IO_ERROR,
335 : 0 : g_io_error_from_errno (errsv),
336 : : _("Error writing to file: %s"),
337 : : g_strerror (errsv));
338 : 0 : goto err_out;
339 : : }
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 : 289 : if (file->priv->tmp_filename)
363 : : {
364 : : /* We need to move the temp file to its final place,
365 : : * and possibly create the backup file
366 : : */
367 : :
368 : 87 : if (file->priv->backup_filename)
369 : : {
370 : 44 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
371 : 1 : goto err_out;
372 : :
373 : : #ifdef G_OS_UNIX
374 : : /* create original -> backup link, the original is then renamed over */
375 : 43 : if (g_unlink (file->priv->backup_filename) != 0 &&
376 : 11 : errno != ENOENT)
377 : : {
378 : 4 : int errsv = errno;
379 : :
380 : 4 : g_set_error (error, G_IO_ERROR,
381 : : G_IO_ERROR_CANT_CREATE_BACKUP,
382 : : _("Error removing old backup link: %s"),
383 : : g_strerror (errsv));
384 : 4 : goto err_out;
385 : : }
386 : :
387 : 39 : if (link (file->priv->original_filename, file->priv->backup_filename) != 0)
388 : : {
389 : : /* link failed or is not supported, try rename */
390 : 0 : if (g_rename (file->priv->original_filename, file->priv->backup_filename) != 0)
391 : : {
392 : 0 : int errsv = errno;
393 : :
394 : 0 : g_set_error (error, G_IO_ERROR,
395 : : G_IO_ERROR_CANT_CREATE_BACKUP,
396 : : _("Error creating backup copy: %s"),
397 : : g_strerror (errsv));
398 : 0 : goto err_out;
399 : : }
400 : : }
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 : : }
415 : :
416 : :
417 : 82 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
418 : 0 : goto err_out;
419 : :
420 : : /* tmp -> original */
421 : 82 : if (g_rename (file->priv->tmp_filename, file->priv->original_filename) != 0)
422 : : {
423 : 0 : int errsv = errno;
424 : :
425 : 0 : g_set_error (error, G_IO_ERROR,
426 : 0 : g_io_error_from_errno (errsv),
427 : : _("Error renaming temporary file: %s"),
428 : : g_strerror (errsv));
429 : 0 : goto err_out;
430 : : }
431 : :
432 : 82 : g_clear_pointer (&file->priv->tmp_filename, g_free);
433 : : }
434 : :
435 : 284 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
436 : 0 : goto err_out;
437 : :
438 : : #ifndef G_OS_WIN32 /* Already did the fstat() and close() above on Win32 */
439 : :
440 : 284 : if (g_local_file_fstat (file->priv->fd, G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, &final_stat) == 0)
441 : 284 : file->priv->etag = _g_local_file_info_create_etag (&final_stat);
442 : :
443 : 284 : if (!g_close (file->priv->fd, NULL))
444 : : {
445 : 0 : int errsv = errno;
446 : :
447 : 0 : g_set_error (error, G_IO_ERROR,
448 : 0 : g_io_error_from_errno (errsv),
449 : : _("Error closing file: %s"),
450 : : g_strerror (errsv));
451 : 0 : goto err_out;
452 : : }
453 : :
454 : : #endif
455 : :
456 : 284 : return TRUE;
457 : 5 : err_out:
458 : :
459 : : #ifndef G_OS_WIN32
460 : : /* A simple try to close the fd in case we fail before the actual close */
461 : 5 : g_close (file->priv->fd, NULL);
462 : : #endif
463 : 5 : if (file->priv->tmp_filename)
464 : 5 : g_unlink (file->priv->tmp_filename);
465 : :
466 : 5 : return FALSE;
467 : : }
468 : :
469 : :
470 : : static gboolean
471 : 289 : g_local_file_output_stream_close (GOutputStream *stream,
472 : : GCancellable *cancellable,
473 : : GError **error)
474 : : {
475 : : GLocalFileOutputStream *file;
476 : :
477 : 289 : file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
478 : :
479 : 289 : if (file->priv->do_close)
480 : 181 : return _g_local_file_output_stream_really_close (file,
481 : : cancellable,
482 : : error);
483 : 108 : return TRUE;
484 : : }
485 : :
486 : : static char *
487 : 3 : g_local_file_output_stream_get_etag (GFileOutputStream *stream)
488 : : {
489 : : GLocalFileOutputStream *file;
490 : :
491 : 3 : file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
492 : :
493 : 6 : return g_strdup (file->priv->etag);
494 : : }
495 : :
496 : : static goffset
497 : 66 : g_local_file_output_stream_tell (GFileOutputStream *stream)
498 : : {
499 : : GLocalFileOutputStream *file;
500 : : off_t pos;
501 : :
502 : 66 : file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
503 : :
504 : 66 : pos = lseek (file->priv->fd, 0, SEEK_CUR);
505 : :
506 : 66 : if (pos == (off_t)-1)
507 : 0 : return 0;
508 : :
509 : 66 : return pos;
510 : : }
511 : :
512 : : static gboolean
513 : 0 : g_local_file_output_stream_can_seek (GFileOutputStream *stream)
514 : : {
515 : : GLocalFileOutputStream *file;
516 : : 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 : : }
527 : :
528 : : static int
529 : 17 : seek_type_to_lseek (GSeekType type)
530 : : {
531 : 17 : switch (type)
532 : : {
533 : 0 : default:
534 : : case G_SEEK_CUR:
535 : 0 : return SEEK_CUR;
536 : :
537 : 14 : case G_SEEK_SET:
538 : 14 : return SEEK_SET;
539 : :
540 : 3 : case G_SEEK_END:
541 : 3 : return SEEK_END;
542 : : }
543 : : }
544 : :
545 : : static gboolean
546 : 17 : g_local_file_output_stream_seek (GFileOutputStream *stream,
547 : : goffset offset,
548 : : GSeekType type,
549 : : GCancellable *cancellable,
550 : : GError **error)
551 : : {
552 : : GLocalFileOutputStream *file;
553 : : off_t pos;
554 : :
555 : 17 : file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
556 : :
557 : 17 : pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type));
558 : :
559 : 17 : if (pos == (off_t)-1)
560 : : {
561 : 0 : int errsv = errno;
562 : :
563 : 0 : g_set_error (error, G_IO_ERROR,
564 : 0 : g_io_error_from_errno (errsv),
565 : : _("Error seeking in file: %s"),
566 : : g_strerror (errsv));
567 : 0 : return FALSE;
568 : : }
569 : :
570 : 17 : return TRUE;
571 : : }
572 : :
573 : : static gboolean
574 : 0 : g_local_file_output_stream_can_truncate (GFileOutputStream *stream)
575 : : {
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 : : }
579 : :
580 : : static gboolean
581 : 0 : g_local_file_output_stream_truncate (GFileOutputStream *stream,
582 : : goffset size,
583 : : GCancellable *cancellable,
584 : : GError **error)
585 : : {
586 : : GLocalFileOutputStream *file;
587 : : 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 : : #endif
597 : :
598 : 0 : if (res == -1)
599 : : {
600 : 0 : int errsv = errno;
601 : :
602 : 0 : if (errsv == EINTR)
603 : : {
604 : 0 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
605 : 0 : return FALSE;
606 : 0 : goto restart;
607 : : }
608 : :
609 : 0 : g_set_error (error, G_IO_ERROR,
610 : 0 : g_io_error_from_errno (errsv),
611 : : _("Error truncating file: %s"),
612 : : g_strerror (errsv));
613 : 0 : return FALSE;
614 : : }
615 : :
616 : 0 : return TRUE;
617 : : }
618 : :
619 : :
620 : : static GFileInfo *
621 : 1 : g_local_file_output_stream_query_info (GFileOutputStream *stream,
622 : : const char *attributes,
623 : : GCancellable *cancellable,
624 : : GError **error)
625 : : {
626 : : GLocalFileOutputStream *file;
627 : :
628 : 1 : file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
629 : :
630 : 1 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
631 : 0 : return NULL;
632 : :
633 : 1 : return _g_local_file_info_get_from_fd (file->priv->fd,
634 : : attributes,
635 : : error);
636 : : }
637 : :
638 : : GFileOutputStream *
639 : 66 : _g_local_file_output_stream_new (int fd)
640 : : {
641 : : GLocalFileOutputStream *stream;
642 : :
643 : 66 : stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
644 : 66 : stream->priv->fd = fd;
645 : 66 : return G_FILE_OUTPUT_STREAM (stream);
646 : : }
647 : :
648 : : static void
649 : 37 : set_error_from_open_errno (const char *filename,
650 : : GError **error)
651 : : {
652 : 37 : int errsv = errno;
653 : :
654 : 37 : 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 : : G_IO_ERROR_INVALID_FILENAME,
658 : : _("Invalid filename"));
659 : : else
660 : : {
661 : 37 : char *display_name = g_filename_display_name (filename);
662 : 74 : g_set_error (error, G_IO_ERROR,
663 : 37 : g_io_error_from_errno (errsv),
664 : : _("Error opening file “%s”: %s"),
665 : : display_name, g_strerror (errsv));
666 : 37 : g_free (display_name);
667 : : }
668 : 37 : }
669 : :
670 : : static GFileOutputStream *
671 : 66 : output_stream_open (const char *filename,
672 : : gint open_flags,
673 : : guint mode,
674 : : GCancellable *cancellable,
675 : : GError **error)
676 : : {
677 : : GLocalFileOutputStream *stream;
678 : : gint fd;
679 : :
680 : 66 : fd = g_open (filename, open_flags, mode);
681 : 66 : if (fd == -1)
682 : : {
683 : 37 : set_error_from_open_errno (filename, error);
684 : 37 : return NULL;
685 : : }
686 : :
687 : 29 : stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
688 : 29 : stream->priv->fd = fd;
689 : 29 : return G_FILE_OUTPUT_STREAM (stream);
690 : : }
691 : :
692 : : GFileOutputStream *
693 : 3 : _g_local_file_output_stream_open (const char *filename,
694 : : gboolean readable,
695 : : GCancellable *cancellable,
696 : : GError **error)
697 : : {
698 : : int open_flags;
699 : :
700 : 3 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
701 : 0 : return NULL;
702 : :
703 : 3 : open_flags = O_BINARY | O_CLOEXEC;
704 : 3 : if (readable)
705 : 3 : open_flags |= O_RDWR;
706 : : else
707 : 0 : open_flags |= O_WRONLY;
708 : :
709 : 3 : return output_stream_open (filename, open_flags, 0666, cancellable, error);
710 : : }
711 : :
712 : : static gint
713 : 393 : mode_from_flags_or_info (GFileCreateFlags flags,
714 : : GFileInfo *reference_info)
715 : : {
716 : 393 : if (flags & G_FILE_CREATE_PRIVATE)
717 : 41 : return 0600;
718 : 352 : else if (reference_info && g_file_info_has_attribute (reference_info, "unix::mode"))
719 : 65 : return g_file_info_get_attribute_uint32 (reference_info, "unix::mode") & (~S_IFMT);
720 : : else
721 : 287 : return 0666;
722 : : }
723 : :
724 : : GFileOutputStream *
725 : 57 : _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 : : {
732 : : int mode;
733 : : int open_flags;
734 : :
735 : 57 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
736 : 0 : return NULL;
737 : :
738 : 57 : mode = mode_from_flags_or_info (flags, reference_info);
739 : :
740 : 57 : open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
741 : 57 : if (readable)
742 : 2 : open_flags |= O_RDWR;
743 : : else
744 : 55 : open_flags |= O_WRONLY;
745 : :
746 : 57 : return output_stream_open (filename, open_flags, mode, cancellable, error);
747 : : }
748 : :
749 : : GFileOutputStream *
750 : 6 : _g_local_file_output_stream_append (const char *filename,
751 : : GFileCreateFlags flags,
752 : : GCancellable *cancellable,
753 : : GError **error)
754 : : {
755 : : int mode;
756 : :
757 : 6 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
758 : 0 : return NULL;
759 : :
760 : 6 : if (flags & G_FILE_CREATE_PRIVATE)
761 : 1 : mode = 0600;
762 : : else
763 : 5 : mode = 0666;
764 : :
765 : 6 : return output_stream_open (filename, O_CREAT | O_APPEND | O_WRONLY | O_BINARY | O_CLOEXEC, mode,
766 : : cancellable, error);
767 : : }
768 : :
769 : : static char *
770 : 57 : create_backup_filename (const char *filename)
771 : : {
772 : 57 : return g_strconcat (filename, BACKUP_EXTENSION, NULL);
773 : : }
774 : :
775 : : #define BUFSIZE 8192 /* size of normal write buffer */
776 : :
777 : : static gboolean
778 : 4 : copy_file_data (gint sfd,
779 : : gint dfd,
780 : : GError **error)
781 : : {
782 : 4 : gboolean ret = TRUE;
783 : : gpointer buffer;
784 : : const gchar *write_buffer;
785 : : gssize bytes_read;
786 : : gssize bytes_to_write;
787 : : gssize bytes_written;
788 : :
789 : 4 : buffer = g_malloc (BUFSIZE);
790 : :
791 : : do
792 : : {
793 : 6 : bytes_read = read (sfd, buffer, BUFSIZE);
794 : 6 : if (bytes_read == -1)
795 : : {
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 : : _("Error reading from file: %s"),
804 : : g_strerror (errsv));
805 : 0 : ret = FALSE;
806 : 0 : break;
807 : : }
808 : :
809 : 6 : bytes_to_write = bytes_read;
810 : 6 : write_buffer = buffer;
811 : :
812 : : do
813 : : {
814 : 6 : bytes_written = write (dfd, write_buffer, bytes_to_write);
815 : 6 : if (bytes_written == -1)
816 : : {
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 : : _("Error writing to file: %s"),
825 : : g_strerror (errsv));
826 : 0 : ret = FALSE;
827 : 0 : break;
828 : : }
829 : :
830 : 6 : bytes_to_write -= bytes_written;
831 : 6 : write_buffer += bytes_written;
832 : : }
833 : 6 : while (bytes_to_write > 0);
834 : :
835 : 6 : } while ((bytes_read != 0) && (ret == TRUE));
836 : :
837 : 4 : g_free (buffer);
838 : :
839 : 4 : return ret;
840 : : }
841 : :
842 : : static int
843 : 119 : 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 : : {
853 : 119 : int fd = -1;
854 : : GLocalFileStat original_stat;
855 : : char *current_etag;
856 : : gboolean is_symlink;
857 : : int open_flags;
858 : : int res;
859 : : int mode;
860 : 119 : int errsv = 0;
861 : 119 : gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION);
862 : :
863 : 119 : 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 : 119 : if (create_backup || readable)
868 : 74 : open_flags = O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC;
869 : : else
870 : 45 : 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 : : #ifdef O_NOFOLLOW
875 : 119 : is_symlink = FALSE;
876 : 119 : fd = g_open (filename, open_flags | O_NOFOLLOW, mode);
877 : 119 : 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 : 119 : if (fd == -1 && errsv == ELOOP)
884 : : #endif
885 : : {
886 : : /* Could be a symlink, or it could be a regular ELOOP error,
887 : : * but then the next open will fail too. */
888 : 18 : is_symlink = TRUE;
889 : 18 : if (!replace_destination_set)
890 : 9 : fd = g_open (filename, open_flags, mode);
891 : : }
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 : 119 : if (fd == -1 &&
904 : 9 : (!is_symlink || !replace_destination_set))
905 : : {
906 : 19 : char *display_name = g_filename_display_name (filename);
907 : 38 : g_set_error (error, G_IO_ERROR,
908 : 19 : g_io_error_from_errno (errsv),
909 : : _("Error opening file “%s”: %s"),
910 : : display_name, g_strerror (errsv));
911 : 19 : g_free (display_name);
912 : 19 : return -1;
913 : : }
914 : :
915 : 100 : if (!is_symlink)
916 : : {
917 : 82 : res = g_local_file_fstat (fd,
918 : : G_LOCAL_FILE_STAT_FIELD_TYPE |
919 : : G_LOCAL_FILE_STAT_FIELD_MODE |
920 : : G_LOCAL_FILE_STAT_FIELD_UID |
921 : : G_LOCAL_FILE_STAT_FIELD_GID |
922 : : G_LOCAL_FILE_STAT_FIELD_MTIME |
923 : : G_LOCAL_FILE_STAT_FIELD_NLINK,
924 : : G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat);
925 : 82 : errsv = errno;
926 : : }
927 : : else
928 : : {
929 : 18 : res = g_local_file_lstat (filename,
930 : : G_LOCAL_FILE_STAT_FIELD_TYPE |
931 : : G_LOCAL_FILE_STAT_FIELD_MODE |
932 : : G_LOCAL_FILE_STAT_FIELD_UID |
933 : : G_LOCAL_FILE_STAT_FIELD_GID |
934 : : G_LOCAL_FILE_STAT_FIELD_MTIME |
935 : : G_LOCAL_FILE_STAT_FIELD_NLINK,
936 : : G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat);
937 : 18 : errsv = errno;
938 : : }
939 : :
940 : 100 : if (res != 0)
941 : : {
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 : : _("Error when getting information for file “%s”: %s"),
946 : : display_name, g_strerror (errsv));
947 : 0 : g_free (display_name);
948 : 0 : goto error;
949 : : }
950 : :
951 : : /* not a regular file */
952 : 100 : if (!S_ISREG (_g_stat_mode (&original_stat)))
953 : : {
954 : 18 : if (S_ISDIR (_g_stat_mode (&original_stat)))
955 : : {
956 : 0 : g_set_error_literal (error,
957 : : G_IO_ERROR,
958 : : G_IO_ERROR_IS_DIRECTORY,
959 : : _("Target file is a directory"));
960 : 0 : goto error;
961 : : }
962 : 18 : else if (!is_symlink ||
963 : : #ifdef S_ISLNK
964 : 18 : !S_ISLNK (_g_stat_mode (&original_stat))
965 : : #else
966 : : FALSE
967 : : #endif
968 : : )
969 : : {
970 : 0 : g_set_error_literal (error,
971 : : G_IO_ERROR,
972 : : G_IO_ERROR_NOT_REGULAR_FILE,
973 : : _("Target file is not a regular file"));
974 : 0 : goto error;
975 : : }
976 : : }
977 : :
978 : 100 : if (etag != NULL)
979 : : {
980 : : GLocalFileStat etag_stat;
981 : : 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 : 5 : if (is_symlink)
986 : : {
987 : 1 : res = g_local_file_stat (filename,
988 : : G_LOCAL_FILE_STAT_FIELD_MTIME,
989 : : G_LOCAL_FILE_STAT_FIELD_ALL, &etag_stat);
990 : 1 : errsv = errno;
991 : :
992 : 1 : if (res != 0)
993 : : {
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 : : _("Error when getting information for file “%s”: %s"),
998 : : display_name, g_strerror (errsv));
999 : 0 : g_free (display_name);
1000 : 4 : goto error;
1001 : : }
1002 : :
1003 : 1 : etag_stat_pointer = &etag_stat;
1004 : : }
1005 : : else
1006 : 4 : etag_stat_pointer = &original_stat;
1007 : :
1008 : : /* Compare the ETags */
1009 : 5 : current_etag = _g_local_file_info_create_etag (etag_stat_pointer);
1010 : 5 : if (strcmp (etag, current_etag) != 0)
1011 : : {
1012 : 4 : g_set_error_literal (error,
1013 : : G_IO_ERROR,
1014 : : G_IO_ERROR_WRONG_ETAG,
1015 : : _("The file was externally modified"));
1016 : 4 : g_free (current_etag);
1017 : 4 : goto error;
1018 : : }
1019 : 1 : g_free (current_etag);
1020 : : }
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 : 138 : if (replace_destination_set ||
1034 : 84 : (!(_g_stat_nlink (&original_stat) > 1) && !is_symlink))
1035 : : {
1036 : : char *dirname, *tmp_filename;
1037 : : int tmpfd;
1038 : :
1039 : 87 : dirname = g_path_get_dirname (filename);
1040 : 87 : tmp_filename = g_build_filename (dirname, ".goutputstream-XXXXXX", NULL);
1041 : 87 : g_free (dirname);
1042 : :
1043 : 87 : tmpfd = g_mkstemp_full (tmp_filename, (readable ? O_RDWR : O_WRONLY) | O_BINARY | O_CLOEXEC, mode);
1044 : 87 : if (tmpfd == -1)
1045 : : {
1046 : 0 : g_free (tmp_filename);
1047 : 0 : goto fallback_strategy;
1048 : : }
1049 : :
1050 : : /* try to keep permissions (unless replacing) */
1051 : :
1052 : 120 : if (!replace_destination_set &&
1053 : : (
1054 : : #ifdef HAVE_FCHOWN
1055 : 66 : fchown (tmpfd, _g_stat_uid (&original_stat), _g_stat_gid (&original_stat)) == -1 ||
1056 : : #endif
1057 : : #ifdef HAVE_FCHMOD
1058 : 33 : fchmod (tmpfd, _g_stat_mode (&original_stat) & ~S_IFMT) == -1 ||
1059 : : #endif
1060 : : 0
1061 : : )
1062 : : )
1063 : : {
1064 : : GLocalFileStat tmp_statbuf;
1065 : : int tres;
1066 : :
1067 : 0 : tres = g_local_file_fstat (tmpfd,
1068 : : G_LOCAL_FILE_STAT_FIELD_TYPE |
1069 : : G_LOCAL_FILE_STAT_FIELD_MODE |
1070 : : G_LOCAL_FILE_STAT_FIELD_UID |
1071 : : G_LOCAL_FILE_STAT_FIELD_GID,
1072 : : 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 : : {
1080 : 0 : g_close (tmpfd, NULL);
1081 : 0 : g_unlink (tmp_filename);
1082 : 0 : g_free (tmp_filename);
1083 : 0 : goto fallback_strategy;
1084 : : }
1085 : : }
1086 : :
1087 : 87 : if (fd >= 0)
1088 : 78 : g_close (fd, NULL);
1089 : 87 : *temp_filename = tmp_filename;
1090 : 87 : return tmpfd;
1091 : : }
1092 : :
1093 : 9 : fallback_strategy:
1094 : :
1095 : 9 : if (create_backup)
1096 : : {
1097 : : #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD)
1098 : : GLocalFileStat tmp_statbuf;
1099 : : #endif
1100 : : char *backup_filename;
1101 : : int bfd;
1102 : :
1103 : 4 : backup_filename = create_backup_filename (filename);
1104 : :
1105 : 4 : if (g_unlink (backup_filename) == -1 && errno != ENOENT)
1106 : : {
1107 : 0 : g_set_error_literal (error,
1108 : : G_IO_ERROR,
1109 : : G_IO_ERROR_CANT_CREATE_BACKUP,
1110 : : _("Backup file creation failed"));
1111 : 0 : g_free (backup_filename);
1112 : 0 : goto error;
1113 : : }
1114 : :
1115 : 4 : bfd = g_open (backup_filename,
1116 : : O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC,
1117 : 4 : _g_stat_mode (&original_stat) & 0777);
1118 : :
1119 : 4 : if (bfd == -1)
1120 : : {
1121 : 0 : g_set_error_literal (error,
1122 : : G_IO_ERROR,
1123 : : G_IO_ERROR_CANT_CREATE_BACKUP,
1124 : : _("Backup file creation failed"));
1125 : 0 : g_free (backup_filename);
1126 : 0 : goto error;
1127 : : }
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 : : #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD)
1134 : 4 : if (g_local_file_fstat (bfd, G_LOCAL_FILE_STAT_FIELD_GID, G_LOCAL_FILE_STAT_FIELD_ALL, &tmp_statbuf) != 0)
1135 : : {
1136 : 0 : g_set_error_literal (error,
1137 : : G_IO_ERROR,
1138 : : G_IO_ERROR_CANT_CREATE_BACKUP,
1139 : : _("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 : : }
1145 : :
1146 : 4 : 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 : : {
1149 : 0 : if (fchmod (bfd,
1150 : 0 : (_g_stat_mode (&original_stat) & 0707) |
1151 : 0 : ((_g_stat_mode (&original_stat) & 07) << 3)) != 0)
1152 : : {
1153 : 0 : g_set_error_literal (error,
1154 : : G_IO_ERROR,
1155 : : G_IO_ERROR_CANT_CREATE_BACKUP,
1156 : : _("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 : : }
1162 : : }
1163 : : #endif
1164 : :
1165 : 4 : if (!copy_file_data (fd, bfd, NULL))
1166 : : {
1167 : 0 : g_set_error_literal (error,
1168 : : G_IO_ERROR,
1169 : : G_IO_ERROR_CANT_CREATE_BACKUP,
1170 : : _("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 : : }
1177 : :
1178 : 4 : g_close (bfd, NULL);
1179 : 4 : g_free (backup_filename);
1180 : :
1181 : : /* Seek back to the start of the file after the backup copy */
1182 : 4 : if (lseek (fd, 0, SEEK_SET) == -1)
1183 : : {
1184 : 0 : errsv = errno;
1185 : :
1186 : 0 : g_set_error (error, G_IO_ERROR,
1187 : 0 : g_io_error_from_errno (errsv),
1188 : : _("Error seeking in file: %s"),
1189 : : g_strerror (errsv));
1190 : 0 : goto error;
1191 : : }
1192 : : }
1193 : :
1194 : 9 : if (replace_destination_set)
1195 : : {
1196 : 0 : g_close (fd, NULL);
1197 : 0 : fd = -1;
1198 : :
1199 : 0 : if (g_unlink (filename) != 0)
1200 : : {
1201 : 0 : errsv = errno;
1202 : :
1203 : 0 : g_set_error (error, G_IO_ERROR,
1204 : 0 : g_io_error_from_errno (errsv),
1205 : : _("Error removing old file: %s"),
1206 : : g_strerror (errsv));
1207 : 0 : goto error;
1208 : : }
1209 : :
1210 : 0 : if (readable)
1211 : 0 : open_flags = O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC;
1212 : : 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 : : {
1217 : : 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 : : _("Error opening file “%s”: %s"),
1224 : : display_name, g_strerror (errsv));
1225 : 0 : g_free (display_name);
1226 : 0 : goto error;
1227 : : }
1228 : : }
1229 : : else
1230 : : {
1231 : : /* Truncate the file at the start */
1232 : : #ifdef G_OS_WIN32
1233 : : if (g_win32_ftruncate (fd, 0) == -1)
1234 : : #else
1235 : 9 : if (ftruncate (fd, 0) == -1)
1236 : : #endif
1237 : : {
1238 : 0 : errsv = errno;
1239 : :
1240 : 0 : g_set_error (error, G_IO_ERROR,
1241 : 0 : g_io_error_from_errno (errsv),
1242 : : _("Error truncating file: %s"),
1243 : : g_strerror (errsv));
1244 : 0 : goto error;
1245 : : }
1246 : : }
1247 : :
1248 : 9 : return fd;
1249 : :
1250 : 4 : error:
1251 : 4 : if (fd >= 0)
1252 : 4 : g_close (fd, NULL);
1253 : :
1254 : 4 : return -1;
1255 : : }
1256 : :
1257 : : GFileOutputStream *
1258 : 217 : _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 : : {
1267 : : GLocalFileOutputStream *stream;
1268 : : int mode;
1269 : : int fd;
1270 : : char *temp_file;
1271 : : gboolean sync_on_close;
1272 : : int open_flags;
1273 : :
1274 : 217 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
1275 : 0 : return NULL;
1276 : :
1277 : 217 : temp_file = NULL;
1278 : :
1279 : 217 : mode = mode_from_flags_or_info (flags, reference_info);
1280 : 217 : sync_on_close = FALSE;
1281 : :
1282 : : /* If the file doesn't exist, create it */
1283 : 217 : open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
1284 : 217 : if (readable)
1285 : 50 : open_flags |= O_RDWR;
1286 : : else
1287 : 167 : open_flags |= O_WRONLY;
1288 : 217 : fd = g_open (filename, open_flags, mode);
1289 : :
1290 : 217 : if (fd == -1 && errno == EEXIST)
1291 : : {
1292 : : /* The file already exists */
1293 : 119 : fd = handle_overwrite_open (filename, readable, etag,
1294 : : create_backup, &temp_file,
1295 : : flags, reference_info,
1296 : : cancellable, error);
1297 : 119 : if (fd == -1)
1298 : 23 : 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 : 96 : sync_on_close = TRUE;
1307 : : }
1308 : 98 : else if (fd == -1)
1309 : : {
1310 : 0 : set_error_from_open_errno (filename, error);
1311 : 0 : return NULL;
1312 : : }
1313 : : #if !defined(HAVE_O_CLOEXEC) && defined(F_SETFD)
1314 : : else
1315 : : fcntl (fd, F_SETFD, FD_CLOEXEC);
1316 : : #endif
1317 : :
1318 : 194 : stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
1319 : 194 : stream->priv->fd = fd;
1320 : 194 : stream->priv->sync_on_close = sync_on_close;
1321 : 194 : stream->priv->tmp_filename = temp_file;
1322 : 194 : if (create_backup)
1323 : 53 : stream->priv->backup_filename = create_backup_filename (filename);
1324 : 194 : stream->priv->original_filename = g_strdup (filename);
1325 : :
1326 : 194 : return G_FILE_OUTPUT_STREAM (stream);
1327 : : }
1328 : :
1329 : : gint
1330 : 166 : _g_local_file_output_stream_get_fd (GLocalFileOutputStream *stream)
1331 : : {
1332 : 166 : g_return_val_if_fail (G_IS_LOCAL_FILE_OUTPUT_STREAM (stream), -1);
1333 : 166 : return stream->priv->fd;
1334 : : }
1335 : :
1336 : : #ifdef G_OS_UNIX
1337 : : static int
1338 : 58 : g_local_file_output_stream_get_fd (GFileDescriptorBased *fd_based)
1339 : : {
1340 : 58 : GLocalFileOutputStream *stream = G_LOCAL_FILE_OUTPUT_STREAM (fd_based);
1341 : 58 : return _g_local_file_output_stream_get_fd (stream);
1342 : : }
1343 : : #endif
|