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 | #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 | #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 | { |
144 | GLocalFileOutputStream *file; |
145 | |
146 | file = G_LOCAL_FILE_OUTPUT_STREAM (object); |
147 | |
148 | g_free (mem: file->priv->tmp_filename); |
149 | g_free (mem: file->priv->original_filename); |
150 | g_free (mem: file->priv->backup_filename); |
151 | g_free (mem: file->priv->etag); |
152 | |
153 | G_OBJECT_CLASS (g_local_file_output_stream_parent_class)->finalize (object); |
154 | } |
155 | |
156 | static void |
157 | g_local_file_output_stream_class_init (GLocalFileOutputStreamClass *klass) |
158 | { |
159 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
160 | GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass); |
161 | GFileOutputStreamClass *file_stream_class = G_FILE_OUTPUT_STREAM_CLASS (klass); |
162 | |
163 | gobject_class->finalize = g_local_file_output_stream_finalize; |
164 | |
165 | stream_class->write_fn = g_local_file_output_stream_write; |
166 | #ifdef G_OS_UNIX |
167 | stream_class->writev_fn = g_local_file_output_stream_writev; |
168 | #endif |
169 | stream_class->close_fn = g_local_file_output_stream_close; |
170 | file_stream_class->query_info = g_local_file_output_stream_query_info; |
171 | file_stream_class->get_etag = g_local_file_output_stream_get_etag; |
172 | file_stream_class->tell = g_local_file_output_stream_tell; |
173 | file_stream_class->can_seek = g_local_file_output_stream_can_seek; |
174 | file_stream_class->seek = g_local_file_output_stream_seek; |
175 | file_stream_class->can_truncate = g_local_file_output_stream_can_truncate; |
176 | file_stream_class->truncate_fn = g_local_file_output_stream_truncate; |
177 | } |
178 | |
179 | #ifdef G_OS_UNIX |
180 | static void |
181 | g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface) |
182 | { |
183 | iface->get_fd = g_local_file_output_stream_get_fd; |
184 | } |
185 | #endif |
186 | |
187 | static void |
188 | g_local_file_output_stream_init (GLocalFileOutputStream *stream) |
189 | { |
190 | stream->priv = g_local_file_output_stream_get_instance_private (self: stream); |
191 | stream->priv->do_close = TRUE; |
192 | } |
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 | { |
201 | GLocalFileOutputStream *file; |
202 | gssize res; |
203 | |
204 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
205 | |
206 | while (1) |
207 | { |
208 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
209 | return -1; |
210 | res = write (fd: file->priv->fd, buf: buffer, n: count); |
211 | if (res == -1) |
212 | { |
213 | int errsv = errno; |
214 | |
215 | if (errsv == EINTR) |
216 | continue; |
217 | |
218 | g_set_error (err: error, G_IO_ERROR, |
219 | code: g_io_error_from_errno (err_no: errsv), |
220 | _("Error writing to file: %s" ), |
221 | g_strerror (errnum: errsv)); |
222 | } |
223 | |
224 | break; |
225 | } |
226 | |
227 | return res; |
228 | } |
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 | #define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \ |
238 | G_SIZEOF_MEMBER (struct iovec, iov_base) == G_SIZEOF_MEMBER (GOutputVector, buffer) && \ |
239 | G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \ |
240 | G_SIZEOF_MEMBER (struct iovec, iov_len) == G_SIZEOF_MEMBER (GOutputVector, size) && \ |
241 | 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 | { |
251 | GLocalFileOutputStream *file; |
252 | gssize res; |
253 | struct iovec *iov; |
254 | |
255 | if (bytes_written) |
256 | *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 | if (n_vectors > G_IOV_MAX) |
262 | n_vectors = G_IOV_MAX; |
263 | |
264 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
265 | |
266 | if (G_OUTPUT_VECTOR_IS_IOVEC) |
267 | { |
268 | /* ABI is compatible */ |
269 | iov = (struct iovec *) vectors; |
270 | } |
271 | else |
272 | { |
273 | gsize i; |
274 | |
275 | /* ABI is incompatible */ |
276 | iov = g_newa (struct iovec, n_vectors); |
277 | for (i = 0; i < n_vectors; i++) |
278 | { |
279 | iov[i].iov_base = (void *)vectors[i].buffer; |
280 | iov[i].iov_len = vectors[i].size; |
281 | } |
282 | } |
283 | |
284 | while (1) |
285 | { |
286 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
287 | return FALSE; |
288 | res = writev (fd: file->priv->fd, iovec: iov, count: n_vectors); |
289 | if (res == -1) |
290 | { |
291 | int errsv = errno; |
292 | |
293 | if (errsv == EINTR) |
294 | continue; |
295 | |
296 | g_set_error (err: error, G_IO_ERROR, |
297 | code: g_io_error_from_errno (err_no: errsv), |
298 | _("Error writing to file: %s" ), |
299 | g_strerror (errnum: errsv)); |
300 | } |
301 | else if (bytes_written) |
302 | { |
303 | *bytes_written = res; |
304 | } |
305 | |
306 | break; |
307 | } |
308 | |
309 | return res != -1; |
310 | } |
311 | #endif |
312 | |
313 | void |
314 | _g_local_file_output_stream_set_do_close (GLocalFileOutputStream *out, |
315 | gboolean do_close) |
316 | { |
317 | out->priv->do_close = do_close; |
318 | } |
319 | |
320 | gboolean |
321 | _g_local_file_output_stream_really_close (GLocalFileOutputStream *file, |
322 | GCancellable *cancellable, |
323 | GError **error) |
324 | { |
325 | GLocalFileStat final_stat; |
326 | |
327 | if (file->priv->sync_on_close && |
328 | g_fsync (fd: file->priv->fd) != 0) |
329 | { |
330 | int errsv = errno; |
331 | |
332 | g_set_error (err: error, G_IO_ERROR, |
333 | code: g_io_error_from_errno (err_no: errsv), |
334 | _("Error writing to file: %s" ), |
335 | g_strerror (errnum: errsv)); |
336 | goto err_out; |
337 | } |
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 | if (file->priv->tmp_filename) |
361 | { |
362 | /* We need to move the temp file to its final place, |
363 | * and possibly create the backup file |
364 | */ |
365 | |
366 | if (file->priv->backup_filename) |
367 | { |
368 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
369 | goto err_out; |
370 | |
371 | #ifdef G_OS_UNIX |
372 | /* create original -> backup link, the original is then renamed over */ |
373 | if (g_unlink (filename: file->priv->backup_filename) != 0 && |
374 | errno != ENOENT) |
375 | { |
376 | int errsv = errno; |
377 | |
378 | g_set_error (err: error, G_IO_ERROR, |
379 | code: G_IO_ERROR_CANT_CREATE_BACKUP, |
380 | _("Error removing old backup link: %s" ), |
381 | g_strerror (errnum: errsv)); |
382 | goto err_out; |
383 | } |
384 | |
385 | if (link (from: file->priv->original_filename, to: file->priv->backup_filename) != 0) |
386 | { |
387 | /* link failed or is not supported, try rename */ |
388 | if (g_rename (old: file->priv->original_filename, new: file->priv->backup_filename) != 0) |
389 | { |
390 | int errsv = errno; |
391 | |
392 | g_set_error (err: error, G_IO_ERROR, |
393 | code: G_IO_ERROR_CANT_CREATE_BACKUP, |
394 | _("Error creating backup copy: %s" ), |
395 | g_strerror (errnum: errsv)); |
396 | goto err_out; |
397 | } |
398 | } |
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 | } |
413 | |
414 | |
415 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
416 | goto err_out; |
417 | |
418 | /* tmp -> original */ |
419 | if (g_rename (old: file->priv->tmp_filename, new: file->priv->original_filename) != 0) |
420 | { |
421 | int errsv = errno; |
422 | |
423 | g_set_error (err: error, G_IO_ERROR, |
424 | code: g_io_error_from_errno (err_no: errsv), |
425 | _("Error renaming temporary file: %s" ), |
426 | g_strerror (errnum: errsv)); |
427 | goto err_out; |
428 | } |
429 | |
430 | g_clear_pointer (&file->priv->tmp_filename, g_free); |
431 | } |
432 | |
433 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
434 | goto err_out; |
435 | |
436 | #ifndef G_OS_WIN32 /* Already did the fstat() and close() above on Win32 */ |
437 | |
438 | if (g_local_file_fstat (fd: file->priv->fd, mask: G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, stat_buf: &final_stat) == 0) |
439 | file->priv->etag = _g_local_file_info_create_etag (statbuf: &final_stat); |
440 | |
441 | if (!g_close (fd: file->priv->fd, NULL)) |
442 | { |
443 | int errsv = errno; |
444 | |
445 | g_set_error (err: error, G_IO_ERROR, |
446 | code: g_io_error_from_errno (err_no: errsv), |
447 | _("Error closing file: %s" ), |
448 | g_strerror (errnum: errsv)); |
449 | goto err_out; |
450 | } |
451 | |
452 | #endif |
453 | |
454 | return TRUE; |
455 | err_out: |
456 | |
457 | #ifndef G_OS_WIN32 |
458 | /* A simple try to close the fd in case we fail before the actual close */ |
459 | g_close (fd: file->priv->fd, NULL); |
460 | #endif |
461 | if (file->priv->tmp_filename) |
462 | g_unlink (filename: file->priv->tmp_filename); |
463 | |
464 | return FALSE; |
465 | } |
466 | |
467 | |
468 | static gboolean |
469 | g_local_file_output_stream_close (GOutputStream *stream, |
470 | GCancellable *cancellable, |
471 | GError **error) |
472 | { |
473 | GLocalFileOutputStream *file; |
474 | |
475 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
476 | |
477 | if (file->priv->do_close) |
478 | return _g_local_file_output_stream_really_close (file, |
479 | cancellable, |
480 | error); |
481 | return TRUE; |
482 | } |
483 | |
484 | static char * |
485 | g_local_file_output_stream_get_etag (GFileOutputStream *stream) |
486 | { |
487 | GLocalFileOutputStream *file; |
488 | |
489 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
490 | |
491 | return g_strdup (str: file->priv->etag); |
492 | } |
493 | |
494 | static goffset |
495 | g_local_file_output_stream_tell (GFileOutputStream *stream) |
496 | { |
497 | GLocalFileOutputStream *file; |
498 | off_t pos; |
499 | |
500 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
501 | |
502 | pos = lseek (fd: file->priv->fd, offset: 0, SEEK_CUR); |
503 | |
504 | if (pos == (off_t)-1) |
505 | return 0; |
506 | |
507 | return pos; |
508 | } |
509 | |
510 | static gboolean |
511 | g_local_file_output_stream_can_seek (GFileOutputStream *stream) |
512 | { |
513 | GLocalFileOutputStream *file; |
514 | off_t pos; |
515 | |
516 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
517 | |
518 | pos = lseek (fd: file->priv->fd, offset: 0, SEEK_CUR); |
519 | |
520 | if (pos == (off_t)-1 && errno == ESPIPE) |
521 | return FALSE; |
522 | |
523 | return TRUE; |
524 | } |
525 | |
526 | static int |
527 | seek_type_to_lseek (GSeekType type) |
528 | { |
529 | switch (type) |
530 | { |
531 | default: |
532 | case G_SEEK_CUR: |
533 | return SEEK_CUR; |
534 | |
535 | case G_SEEK_SET: |
536 | return SEEK_SET; |
537 | |
538 | case G_SEEK_END: |
539 | return SEEK_END; |
540 | } |
541 | } |
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 | { |
550 | GLocalFileOutputStream *file; |
551 | off_t pos; |
552 | |
553 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
554 | |
555 | pos = lseek (fd: file->priv->fd, offset: offset, whence: seek_type_to_lseek (type)); |
556 | |
557 | if (pos == (off_t)-1) |
558 | { |
559 | int errsv = errno; |
560 | |
561 | g_set_error (err: error, G_IO_ERROR, |
562 | code: g_io_error_from_errno (err_no: errsv), |
563 | _("Error seeking in file: %s" ), |
564 | g_strerror (errnum: errsv)); |
565 | return FALSE; |
566 | } |
567 | |
568 | return TRUE; |
569 | } |
570 | |
571 | static gboolean |
572 | g_local_file_output_stream_can_truncate (GFileOutputStream *stream) |
573 | { |
574 | /* We can't truncate pipes and stuff where we can't seek */ |
575 | return g_local_file_output_stream_can_seek (stream); |
576 | } |
577 | |
578 | static gboolean |
579 | g_local_file_output_stream_truncate (GFileOutputStream *stream, |
580 | goffset size, |
581 | GCancellable *cancellable, |
582 | GError **error) |
583 | { |
584 | GLocalFileOutputStream *file; |
585 | int res; |
586 | |
587 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
588 | |
589 | restart: |
590 | #ifdef G_OS_WIN32 |
591 | res = g_win32_ftruncate (file->priv->fd, size); |
592 | #else |
593 | res = ftruncate (fd: file->priv->fd, length: size); |
594 | #endif |
595 | |
596 | if (res == -1) |
597 | { |
598 | int errsv = errno; |
599 | |
600 | if (errsv == EINTR) |
601 | { |
602 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
603 | return FALSE; |
604 | goto restart; |
605 | } |
606 | |
607 | g_set_error (err: error, G_IO_ERROR, |
608 | code: g_io_error_from_errno (err_no: errsv), |
609 | _("Error truncating file: %s" ), |
610 | g_strerror (errnum: errsv)); |
611 | return FALSE; |
612 | } |
613 | |
614 | return TRUE; |
615 | } |
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 | { |
624 | GLocalFileOutputStream *file; |
625 | |
626 | file = G_LOCAL_FILE_OUTPUT_STREAM (stream); |
627 | |
628 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
629 | return NULL; |
630 | |
631 | return _g_local_file_info_get_from_fd (fd: file->priv->fd, |
632 | attributes, |
633 | error); |
634 | } |
635 | |
636 | GFileOutputStream * |
637 | _g_local_file_output_stream_new (int fd) |
638 | { |
639 | GLocalFileOutputStream *stream; |
640 | |
641 | stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL); |
642 | stream->priv->fd = fd; |
643 | return G_FILE_OUTPUT_STREAM (stream); |
644 | } |
645 | |
646 | static void |
647 | set_error_from_open_errno (const char *filename, |
648 | GError **error) |
649 | { |
650 | int errsv = errno; |
651 | |
652 | if (errsv == EINVAL) |
653 | /* This must be an invalid filename, on e.g. FAT */ |
654 | g_set_error_literal (err: error, G_IO_ERROR, |
655 | code: G_IO_ERROR_INVALID_FILENAME, |
656 | _("Invalid filename" )); |
657 | else |
658 | { |
659 | char *display_name = g_filename_display_name (filename); |
660 | g_set_error (err: error, G_IO_ERROR, |
661 | code: g_io_error_from_errno (err_no: errsv), |
662 | _("Error opening file ā%sā: %s" ), |
663 | display_name, g_strerror (errnum: errsv)); |
664 | g_free (mem: display_name); |
665 | } |
666 | } |
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 | { |
675 | GLocalFileOutputStream *stream; |
676 | gint fd; |
677 | |
678 | fd = g_open (file: filename, oflag: open_flags, mode); |
679 | if (fd == -1) |
680 | { |
681 | set_error_from_open_errno (filename, error); |
682 | return NULL; |
683 | } |
684 | |
685 | stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL); |
686 | stream->priv->fd = fd; |
687 | return G_FILE_OUTPUT_STREAM (stream); |
688 | } |
689 | |
690 | GFileOutputStream * |
691 | _g_local_file_output_stream_open (const char *filename, |
692 | gboolean readable, |
693 | GCancellable *cancellable, |
694 | GError **error) |
695 | { |
696 | int open_flags; |
697 | |
698 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
699 | return NULL; |
700 | |
701 | open_flags = O_BINARY; |
702 | if (readable) |
703 | open_flags |= O_RDWR; |
704 | else |
705 | open_flags |= O_WRONLY; |
706 | |
707 | return output_stream_open (filename, open_flags, mode: 0666, cancellable, error); |
708 | } |
709 | |
710 | static gint |
711 | mode_from_flags_or_info (GFileCreateFlags flags, |
712 | GFileInfo *reference_info) |
713 | { |
714 | if (flags & G_FILE_CREATE_PRIVATE) |
715 | return 0600; |
716 | else if (reference_info && g_file_info_has_attribute (info: reference_info, attribute: "unix::mode" )) |
717 | return g_file_info_get_attribute_uint32 (info: reference_info, attribute: "unix::mode" ) & (~S_IFMT); |
718 | else |
719 | return 0666; |
720 | } |
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 | { |
730 | int mode; |
731 | int open_flags; |
732 | |
733 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
734 | return NULL; |
735 | |
736 | mode = mode_from_flags_or_info (flags, reference_info); |
737 | |
738 | open_flags = O_CREAT | O_EXCL | O_BINARY; |
739 | if (readable) |
740 | open_flags |= O_RDWR; |
741 | else |
742 | open_flags |= O_WRONLY; |
743 | |
744 | return output_stream_open (filename, open_flags, mode, cancellable, error); |
745 | } |
746 | |
747 | GFileOutputStream * |
748 | _g_local_file_output_stream_append (const char *filename, |
749 | GFileCreateFlags flags, |
750 | GCancellable *cancellable, |
751 | GError **error) |
752 | { |
753 | int mode; |
754 | |
755 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
756 | return NULL; |
757 | |
758 | if (flags & G_FILE_CREATE_PRIVATE) |
759 | mode = 0600; |
760 | else |
761 | mode = 0666; |
762 | |
763 | return output_stream_open (filename, O_CREAT | O_APPEND | O_WRONLY | O_BINARY, mode, |
764 | cancellable, error); |
765 | } |
766 | |
767 | static char * |
768 | create_backup_filename (const char *filename) |
769 | { |
770 | return g_strconcat (string1: filename, BACKUP_EXTENSION, NULL); |
771 | } |
772 | |
773 | #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 | { |
780 | gboolean ret = TRUE; |
781 | gpointer buffer; |
782 | const gchar *write_buffer; |
783 | gssize bytes_read; |
784 | gssize bytes_to_write; |
785 | gssize bytes_written; |
786 | |
787 | buffer = g_malloc (BUFSIZE); |
788 | |
789 | do |
790 | { |
791 | bytes_read = read (fd: sfd, buf: buffer, BUFSIZE); |
792 | if (bytes_read == -1) |
793 | { |
794 | int errsv = errno; |
795 | |
796 | if (errsv == EINTR) |
797 | continue; |
798 | |
799 | g_set_error (err: error, G_IO_ERROR, |
800 | code: g_io_error_from_errno (err_no: errsv), |
801 | _("Error reading from file: %s" ), |
802 | g_strerror (errnum: errsv)); |
803 | ret = FALSE; |
804 | break; |
805 | } |
806 | |
807 | bytes_to_write = bytes_read; |
808 | write_buffer = buffer; |
809 | |
810 | do |
811 | { |
812 | bytes_written = write (fd: dfd, buf: write_buffer, n: bytes_to_write); |
813 | if (bytes_written == -1) |
814 | { |
815 | int errsv = errno; |
816 | |
817 | if (errsv == EINTR) |
818 | continue; |
819 | |
820 | g_set_error (err: error, G_IO_ERROR, |
821 | code: g_io_error_from_errno (err_no: errsv), |
822 | _("Error writing to file: %s" ), |
823 | g_strerror (errnum: errsv)); |
824 | ret = FALSE; |
825 | break; |
826 | } |
827 | |
828 | bytes_to_write -= bytes_written; |
829 | write_buffer += bytes_written; |
830 | } |
831 | while (bytes_to_write > 0); |
832 | |
833 | } while ((bytes_read != 0) && (ret == TRUE)); |
834 | |
835 | g_free (mem: buffer); |
836 | |
837 | return ret; |
838 | } |
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 | { |
851 | int fd = -1; |
852 | GLocalFileStat original_stat; |
853 | char *current_etag; |
854 | gboolean is_symlink; |
855 | int open_flags; |
856 | int res; |
857 | int mode; |
858 | int errsv; |
859 | gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION); |
860 | |
861 | 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 | if (create_backup || readable) |
866 | open_flags = O_RDWR | O_CREAT | O_BINARY; |
867 | else |
868 | 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 | #ifdef O_NOFOLLOW |
873 | is_symlink = FALSE; |
874 | fd = g_open (file: filename, oflag: open_flags | O_NOFOLLOW, mode); |
875 | 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 | if (fd == -1 && errsv == ELOOP) |
882 | #endif |
883 | { |
884 | /* Could be a symlink, or it could be a regular ELOOP error, |
885 | * but then the next open will fail too. */ |
886 | is_symlink = TRUE; |
887 | if (!replace_destination_set) |
888 | fd = g_open (file: filename, oflag: open_flags, mode); |
889 | } |
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 | if (fd == -1 && |
902 | (!is_symlink || !replace_destination_set)) |
903 | { |
904 | char *display_name = g_filename_display_name (filename); |
905 | g_set_error (err: error, G_IO_ERROR, |
906 | code: g_io_error_from_errno (err_no: errsv), |
907 | _("Error opening file ā%sā: %s" ), |
908 | display_name, g_strerror (errnum: errsv)); |
909 | g_free (mem: display_name); |
910 | return -1; |
911 | } |
912 | |
913 | if (!is_symlink) |
914 | { |
915 | res = g_local_file_fstat (fd, |
916 | mask: G_LOCAL_FILE_STAT_FIELD_TYPE | |
917 | G_LOCAL_FILE_STAT_FIELD_MODE | |
918 | G_LOCAL_FILE_STAT_FIELD_UID | |
919 | G_LOCAL_FILE_STAT_FIELD_GID | |
920 | G_LOCAL_FILE_STAT_FIELD_MTIME | |
921 | G_LOCAL_FILE_STAT_FIELD_NLINK, |
922 | G_LOCAL_FILE_STAT_FIELD_ALL, stat_buf: &original_stat); |
923 | errsv = errno; |
924 | } |
925 | else |
926 | { |
927 | res = g_local_file_lstat (path: filename, |
928 | mask: G_LOCAL_FILE_STAT_FIELD_TYPE | |
929 | G_LOCAL_FILE_STAT_FIELD_MODE | |
930 | G_LOCAL_FILE_STAT_FIELD_UID | |
931 | G_LOCAL_FILE_STAT_FIELD_GID | |
932 | G_LOCAL_FILE_STAT_FIELD_MTIME | |
933 | G_LOCAL_FILE_STAT_FIELD_NLINK, |
934 | G_LOCAL_FILE_STAT_FIELD_ALL, stat_buf: &original_stat); |
935 | errsv = errno; |
936 | } |
937 | |
938 | if (res != 0) |
939 | { |
940 | char *display_name = g_filename_display_name (filename); |
941 | g_set_error (err: error, G_IO_ERROR, |
942 | code: g_io_error_from_errno (err_no: errsv), |
943 | _("Error when getting information for file ā%sā: %s" ), |
944 | display_name, g_strerror (errnum: errsv)); |
945 | g_free (mem: display_name); |
946 | goto error; |
947 | } |
948 | |
949 | /* not a regular file */ |
950 | if (!S_ISREG (_g_stat_mode (&original_stat))) |
951 | { |
952 | if (S_ISDIR (_g_stat_mode (&original_stat))) |
953 | { |
954 | g_set_error_literal (err: error, |
955 | G_IO_ERROR, |
956 | code: G_IO_ERROR_IS_DIRECTORY, |
957 | _("Target file is a directory" )); |
958 | goto error; |
959 | } |
960 | else if (!is_symlink || |
961 | #ifdef S_ISLNK |
962 | !S_ISLNK (_g_stat_mode (&original_stat)) |
963 | #else |
964 | FALSE |
965 | #endif |
966 | ) |
967 | { |
968 | g_set_error_literal (err: error, |
969 | G_IO_ERROR, |
970 | code: G_IO_ERROR_NOT_REGULAR_FILE, |
971 | _("Target file is not a regular file" )); |
972 | goto error; |
973 | } |
974 | } |
975 | |
976 | if (etag != NULL) |
977 | { |
978 | GLocalFileStat etag_stat; |
979 | 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 | if (is_symlink) |
984 | { |
985 | res = g_local_file_stat (path: filename, |
986 | mask: G_LOCAL_FILE_STAT_FIELD_MTIME, |
987 | G_LOCAL_FILE_STAT_FIELD_ALL, stat_buf: &etag_stat); |
988 | errsv = errno; |
989 | |
990 | if (res != 0) |
991 | { |
992 | char *display_name = g_filename_display_name (filename); |
993 | g_set_error (err: error, G_IO_ERROR, |
994 | code: g_io_error_from_errno (err_no: errsv), |
995 | _("Error when getting information for file ā%sā: %s" ), |
996 | display_name, g_strerror (errnum: errsv)); |
997 | g_free (mem: display_name); |
998 | goto error; |
999 | } |
1000 | |
1001 | etag_stat_pointer = &etag_stat; |
1002 | } |
1003 | else |
1004 | etag_stat_pointer = &original_stat; |
1005 | |
1006 | /* Compare the ETags */ |
1007 | current_etag = _g_local_file_info_create_etag (statbuf: etag_stat_pointer); |
1008 | if (strcmp (s1: etag, s2: current_etag) != 0) |
1009 | { |
1010 | g_set_error_literal (err: error, |
1011 | G_IO_ERROR, |
1012 | code: G_IO_ERROR_WRONG_ETAG, |
1013 | _("The file was externally modified" )); |
1014 | g_free (mem: current_etag); |
1015 | goto error; |
1016 | } |
1017 | g_free (mem: current_etag); |
1018 | } |
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 | if (replace_destination_set || |
1032 | (!(_g_stat_nlink (buf: &original_stat) > 1) && !is_symlink)) |
1033 | { |
1034 | char *dirname, *tmp_filename; |
1035 | int tmpfd; |
1036 | |
1037 | dirname = g_path_get_dirname (file_name: filename); |
1038 | tmp_filename = g_build_filename (first_element: dirname, ".goutputstream-XXXXXX" , NULL); |
1039 | g_free (mem: dirname); |
1040 | |
1041 | tmpfd = g_mkstemp_full (tmpl: tmp_filename, flags: (readable ? O_RDWR : O_WRONLY) | O_BINARY, mode); |
1042 | if (tmpfd == -1) |
1043 | { |
1044 | g_free (mem: tmp_filename); |
1045 | goto fallback_strategy; |
1046 | } |
1047 | |
1048 | /* try to keep permissions (unless replacing) */ |
1049 | |
1050 | if (!replace_destination_set && |
1051 | ( |
1052 | #ifdef HAVE_FCHOWN |
1053 | fchown (fd: tmpfd, owner: _g_stat_uid (buf: &original_stat), group: _g_stat_gid (buf: &original_stat)) == -1 || |
1054 | #endif |
1055 | #ifdef HAVE_FCHMOD |
1056 | fchmod (fd: tmpfd, mode: _g_stat_mode (buf: &original_stat) & ~S_IFMT) == -1 || |
1057 | #endif |
1058 | 0 |
1059 | ) |
1060 | ) |
1061 | { |
1062 | GLocalFileStat tmp_statbuf; |
1063 | int tres; |
1064 | |
1065 | tres = g_local_file_fstat (fd: tmpfd, |
1066 | mask: G_LOCAL_FILE_STAT_FIELD_TYPE | |
1067 | G_LOCAL_FILE_STAT_FIELD_MODE | |
1068 | G_LOCAL_FILE_STAT_FIELD_UID | |
1069 | G_LOCAL_FILE_STAT_FIELD_GID, |
1070 | G_LOCAL_FILE_STAT_FIELD_ALL, stat_buf: &tmp_statbuf); |
1071 | |
1072 | /* Check that we really needed to change something */ |
1073 | if (tres != 0 || |
1074 | _g_stat_uid (buf: &original_stat) != _g_stat_uid (buf: &tmp_statbuf) || |
1075 | _g_stat_gid (buf: &original_stat) != _g_stat_gid (buf: &tmp_statbuf) || |
1076 | _g_stat_mode (buf: &original_stat) != _g_stat_mode (buf: &tmp_statbuf)) |
1077 | { |
1078 | g_close (fd: tmpfd, NULL); |
1079 | g_unlink (filename: tmp_filename); |
1080 | g_free (mem: tmp_filename); |
1081 | goto fallback_strategy; |
1082 | } |
1083 | } |
1084 | |
1085 | if (fd >= 0) |
1086 | g_close (fd, NULL); |
1087 | *temp_filename = tmp_filename; |
1088 | return tmpfd; |
1089 | } |
1090 | |
1091 | fallback_strategy: |
1092 | |
1093 | if (create_backup) |
1094 | { |
1095 | #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) |
1096 | GLocalFileStat tmp_statbuf; |
1097 | #endif |
1098 | char *backup_filename; |
1099 | int bfd; |
1100 | |
1101 | backup_filename = create_backup_filename (filename); |
1102 | |
1103 | if (g_unlink (filename: backup_filename) == -1 && errno != ENOENT) |
1104 | { |
1105 | g_set_error_literal (err: error, |
1106 | G_IO_ERROR, |
1107 | code: G_IO_ERROR_CANT_CREATE_BACKUP, |
1108 | _("Backup file creation failed" )); |
1109 | g_free (mem: backup_filename); |
1110 | goto error; |
1111 | } |
1112 | |
1113 | bfd = g_open (file: backup_filename, |
1114 | O_WRONLY | O_CREAT | O_EXCL | O_BINARY, |
1115 | _g_stat_mode (buf: &original_stat) & 0777); |
1116 | |
1117 | if (bfd == -1) |
1118 | { |
1119 | g_set_error_literal (err: error, |
1120 | G_IO_ERROR, |
1121 | code: G_IO_ERROR_CANT_CREATE_BACKUP, |
1122 | _("Backup file creation failed" )); |
1123 | g_free (mem: backup_filename); |
1124 | goto error; |
1125 | } |
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 | #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) |
1132 | if (g_local_file_fstat (fd: bfd, mask: G_LOCAL_FILE_STAT_FIELD_GID, G_LOCAL_FILE_STAT_FIELD_ALL, stat_buf: &tmp_statbuf) != 0) |
1133 | { |
1134 | g_set_error_literal (err: error, |
1135 | G_IO_ERROR, |
1136 | code: G_IO_ERROR_CANT_CREATE_BACKUP, |
1137 | _("Backup file creation failed" )); |
1138 | g_unlink (filename: backup_filename); |
1139 | g_close (fd: bfd, NULL); |
1140 | g_free (mem: backup_filename); |
1141 | goto error; |
1142 | } |
1143 | |
1144 | if ((_g_stat_gid (buf: &original_stat) != _g_stat_gid (buf: &tmp_statbuf)) && |
1145 | fchown (fd: bfd, owner: (uid_t) -1, group: _g_stat_gid (buf: &original_stat)) != 0) |
1146 | { |
1147 | if (fchmod (fd: bfd, |
1148 | mode: (_g_stat_mode (buf: &original_stat) & 0707) | |
1149 | ((_g_stat_mode (buf: &original_stat) & 07) << 3)) != 0) |
1150 | { |
1151 | g_set_error_literal (err: error, |
1152 | G_IO_ERROR, |
1153 | code: G_IO_ERROR_CANT_CREATE_BACKUP, |
1154 | _("Backup file creation failed" )); |
1155 | g_unlink (filename: backup_filename); |
1156 | g_close (fd: bfd, NULL); |
1157 | g_free (mem: backup_filename); |
1158 | goto error; |
1159 | } |
1160 | } |
1161 | #endif |
1162 | |
1163 | if (!copy_file_data (sfd: fd, dfd: bfd, NULL)) |
1164 | { |
1165 | g_set_error_literal (err: error, |
1166 | G_IO_ERROR, |
1167 | code: G_IO_ERROR_CANT_CREATE_BACKUP, |
1168 | _("Backup file creation failed" )); |
1169 | g_unlink (filename: backup_filename); |
1170 | g_close (fd: bfd, NULL); |
1171 | g_free (mem: backup_filename); |
1172 | |
1173 | goto error; |
1174 | } |
1175 | |
1176 | g_close (fd: bfd, NULL); |
1177 | g_free (mem: backup_filename); |
1178 | |
1179 | /* Seek back to the start of the file after the backup copy */ |
1180 | if (lseek (fd: fd, offset: 0, SEEK_SET) == -1) |
1181 | { |
1182 | int errsv = errno; |
1183 | |
1184 | g_set_error (err: error, G_IO_ERROR, |
1185 | code: g_io_error_from_errno (err_no: errsv), |
1186 | _("Error seeking in file: %s" ), |
1187 | g_strerror (errnum: errsv)); |
1188 | goto error; |
1189 | } |
1190 | } |
1191 | |
1192 | if (replace_destination_set) |
1193 | { |
1194 | g_close (fd, NULL); |
1195 | |
1196 | if (g_unlink (filename) != 0) |
1197 | { |
1198 | int errsv = errno; |
1199 | |
1200 | g_set_error (err: error, G_IO_ERROR, |
1201 | code: g_io_error_from_errno (err_no: errsv), |
1202 | _("Error removing old file: %s" ), |
1203 | g_strerror (errnum: errsv)); |
1204 | goto error; |
1205 | } |
1206 | |
1207 | if (readable) |
1208 | open_flags = O_RDWR | O_CREAT | O_BINARY; |
1209 | else |
1210 | open_flags = O_WRONLY | O_CREAT | O_BINARY; |
1211 | fd = g_open (file: filename, oflag: open_flags, mode); |
1212 | if (fd == -1) |
1213 | { |
1214 | int errsv = errno; |
1215 | char *display_name = g_filename_display_name (filename); |
1216 | g_set_error (err: error, G_IO_ERROR, |
1217 | code: g_io_error_from_errno (err_no: errsv), |
1218 | _("Error opening file ā%sā: %s" ), |
1219 | display_name, g_strerror (errnum: errsv)); |
1220 | g_free (mem: display_name); |
1221 | goto error; |
1222 | } |
1223 | } |
1224 | else |
1225 | { |
1226 | /* Truncate the file at the start */ |
1227 | #ifdef G_OS_WIN32 |
1228 | if (g_win32_ftruncate (fd, 0) == -1) |
1229 | #else |
1230 | if (ftruncate (fd: fd, length: 0) == -1) |
1231 | #endif |
1232 | { |
1233 | int errsv = errno; |
1234 | |
1235 | g_set_error (err: error, G_IO_ERROR, |
1236 | code: g_io_error_from_errno (err_no: errsv), |
1237 | _("Error truncating file: %s" ), |
1238 | g_strerror (errnum: errsv)); |
1239 | goto error; |
1240 | } |
1241 | } |
1242 | |
1243 | return fd; |
1244 | |
1245 | error: |
1246 | if (fd >= 0) |
1247 | g_close (fd, NULL); |
1248 | |
1249 | return -1; |
1250 | } |
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 | { |
1262 | GLocalFileOutputStream *stream; |
1263 | int mode; |
1264 | int fd; |
1265 | char *temp_file; |
1266 | gboolean sync_on_close; |
1267 | int open_flags; |
1268 | |
1269 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
1270 | return NULL; |
1271 | |
1272 | temp_file = NULL; |
1273 | |
1274 | mode = mode_from_flags_or_info (flags, reference_info); |
1275 | sync_on_close = FALSE; |
1276 | |
1277 | /* If the file doesn't exist, create it */ |
1278 | open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC; |
1279 | if (readable) |
1280 | open_flags |= O_RDWR; |
1281 | else |
1282 | open_flags |= O_WRONLY; |
1283 | fd = g_open (file: filename, oflag: open_flags, mode); |
1284 | |
1285 | if (fd == -1 && errno == EEXIST) |
1286 | { |
1287 | /* The file already exists */ |
1288 | fd = handle_overwrite_open (filename, readable, etag, |
1289 | create_backup, temp_filename: &temp_file, |
1290 | flags, reference_info, |
1291 | cancellable, error); |
1292 | if (fd == -1) |
1293 | 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 | sync_on_close = TRUE; |
1302 | } |
1303 | else if (fd == -1) |
1304 | { |
1305 | set_error_from_open_errno (filename, error); |
1306 | return NULL; |
1307 | } |
1308 | #if !defined(HAVE_O_CLOEXEC) && defined(F_SETFD) |
1309 | else |
1310 | fcntl (fd, F_SETFD, FD_CLOEXEC); |
1311 | #endif |
1312 | |
1313 | stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL); |
1314 | stream->priv->fd = fd; |
1315 | stream->priv->sync_on_close = sync_on_close; |
1316 | stream->priv->tmp_filename = temp_file; |
1317 | if (create_backup) |
1318 | stream->priv->backup_filename = create_backup_filename (filename); |
1319 | stream->priv->original_filename = g_strdup (str: filename); |
1320 | |
1321 | return G_FILE_OUTPUT_STREAM (stream); |
1322 | } |
1323 | |
1324 | gint |
1325 | _g_local_file_output_stream_get_fd (GLocalFileOutputStream *stream) |
1326 | { |
1327 | g_return_val_if_fail (G_IS_LOCAL_FILE_OUTPUT_STREAM (stream), -1); |
1328 | return stream->priv->fd; |
1329 | } |
1330 | |
1331 | #ifdef G_OS_UNIX |
1332 | static int |
1333 | g_local_file_output_stream_get_fd (GFileDescriptorBased *fd_based) |
1334 | { |
1335 | GLocalFileOutputStream *stream = G_LOCAL_FILE_OUTPUT_STREAM (fd_based); |
1336 | return _g_local_file_output_stream_get_fd (stream); |
1337 | } |
1338 | #endif |
1339 | |