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 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, write to the 17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 18 * Boston, MA 02111-1307, USA. 19 * 20 * Author: Alexander Larsson <alexl (at) redhat.com> 21 */ 22 23 #include "config.h" 24 25 #include <glib.h> 26 #include <gfileoutputstream.h> 27 #include <gseekable.h> 28 #include "gsimpleasyncresult.h" 29 #include "gasyncresult.h" 30 #include "gcancellable.h" 31 #include "gioerror.h" 32 #include "glibintl.h" 33 34 #include "gioalias.h" 35 36 /** 37 * SECTION:gfileoutputstream 38 * @short_description: File output streaming operations 39 * @include: gio/gio.h 40 * @see_also: #GOutputStream, #GDataOutputStream, #GSeekable 41 * 42 * GFileOutputStream provides output streams that write their 43 * content to a file. 44 * 45 * GFileOutputStream implements #GSeekable, which allows the output 46 * stream to jump to arbitrary positions in the file and to truncate 47 * the file, provided the filesystem of the file supports these 48 * operations. In addition to the generic g_seekable_ API, 49 * GFileOutputStream has its own API for seeking and positioning. 50 * To find the position of a file output stream, use 51 * g_file_output_stream_tell(). To find out if a file output 52 * stream supports seeking, use g_file_output_stream_can_seek(). 53 * To position a file output stream, use g_file_output_stream_seek(). 54 * To find out if a file output stream supports truncating, use 55 * g_file_output_stream_can_truncate(). To truncate a file output 56 * stream, use g_file_output_stream_truncate(). 57 **/ 58 59 static void g_file_output_stream_seekable_iface_init (GSeekableIface *iface); 60 static goffset g_file_output_stream_seekable_tell (GSeekable *seekable); 61 static gboolean g_file_output_stream_seekable_can_seek (GSeekable *seekable); 62 static gboolean g_file_output_stream_seekable_seek (GSeekable *seekable, 63 goffset offset, 64 GSeekType type, 65 GCancellable *cancellable, 66 GError **error); 67 static gboolean g_file_output_stream_seekable_can_truncate (GSeekable *seekable); 68 static gboolean g_file_output_stream_seekable_truncate (GSeekable *seekable, 69 goffset offset, 70 GCancellable *cancellable, 71 GError **error); 72 static void g_file_output_stream_real_query_info_async (GFileOutputStream *stream, 73 const char *attributes, 74 int io_priority, 75 GCancellable *cancellable, 76 GAsyncReadyCallback callback, 77 gpointer user_data); 78 static GFileInfo *g_file_output_stream_real_query_info_finish (GFileOutputStream *stream, 79 GAsyncResult *result, 80 GError **error); 81 82 G_DEFINE_TYPE_WITH_CODE (GFileOutputStream, g_file_output_stream, G_TYPE_OUTPUT_STREAM, 83 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, 84 g_file_output_stream_seekable_iface_init)); 85 86 struct _GFileOutputStreamPrivate { 87 GAsyncReadyCallback outstanding_callback; 88 }; 89 90 static void 91 g_file_output_stream_class_init (GFileOutputStreamClass *klass) 92 { 93 g_type_class_add_private (klass, sizeof (GFileOutputStreamPrivate)); 94 95 klass->query_info_async = g_file_output_stream_real_query_info_async; 96 klass->query_info_finish = g_file_output_stream_real_query_info_finish; 97 } 98 99 static void 100 g_file_output_stream_seekable_iface_init (GSeekableIface *iface) 101 { 102 iface->tell = g_file_output_stream_seekable_tell; 103 iface->can_seek = g_file_output_stream_seekable_can_seek; 104 iface->seek = g_file_output_stream_seekable_seek; 105 iface->can_truncate = g_file_output_stream_seekable_can_truncate; 106 iface->truncate_fn = g_file_output_stream_seekable_truncate; 107 } 108 109 static void 110 g_file_output_stream_init (GFileOutputStream *stream) 111 { 112 stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, 113 G_TYPE_FILE_OUTPUT_STREAM, 114 GFileOutputStreamPrivate); 115 } 116 117 /** 118 * g_file_output_stream_query_info: 119 * @stream: a #GFileOutputStream. 120 * @attributes: a file attribute query string. 121 * @cancellable: optional #GCancellable object, %NULL to ignore. 122 * @error: a #GError, %NULL to ignore. 123 * 124 * Queries a file output stream for the given @attributes. 125 * This function blocks while querying the stream. For the asynchronous 126 * version of this function, see g_file_output_stream_query_info_async(). 127 * While the stream is blocked, the stream will set the pending flag 128 * internally, and any other operations on the stream will fail with 129 * %G_IO_ERROR_PENDING. 130 * 131 * Can fail if the stream was already closed (with @error being set to 132 * %G_IO_ERROR_CLOSED), the stream has pending operations (with @error being 133 * set to %G_IO_ERROR_PENDING), or if querying info is not supported for 134 * the stream's interface (with @error being set to %G_IO_ERROR_NOT_SUPPORTED). In 135 * all cases of failure, %NULL will be returned. 136 * 137 * If @cancellable is not %NULL, then the operation can be cancelled by 138 * triggering the cancellable object from another thread. If the operation 139 * was cancelled, the error %G_IO_ERROR_CANCELLED will be set, and %NULL will 140 * be returned. 141 * 142 * Returns: a #GFileInfo for the @stream, or %NULL on error. 143 **/ 144 GFileInfo * 145 g_file_output_stream_query_info (GFileOutputStream *stream, 146 const char *attributes, 147 GCancellable *cancellable, 148 GError **error) 149 { 150 GFileOutputStreamClass *class; 151 GOutputStream *output_stream; 152 GFileInfo *info; 153 154 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL); 155 156 output_stream = G_OUTPUT_STREAM (stream); 157 158 if (!g_output_stream_set_pending (output_stream, error)) 159 return NULL; 160 161 info = NULL; 162 163 if (cancellable) 164 g_cancellable_push_current (cancellable); 165 166 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 167 if (class->query_info) 168 info = class->query_info (stream, attributes, cancellable, error); 169 else 170 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 171 _("Stream doesn't support query_info")); 172 173 if (cancellable) 174 g_cancellable_pop_current (cancellable); 175 176 g_output_stream_clear_pending (output_stream); 177 178 return info; 179 } 180 181 static void 182 async_ready_callback_wrapper (GObject *source_object, 183 GAsyncResult *res, 184 gpointer user_data) 185 { 186 GFileOutputStream *stream = G_FILE_OUTPUT_STREAM (source_object); 187 188 g_output_stream_clear_pending (G_OUTPUT_STREAM (stream)); 189 if (stream->priv->outstanding_callback) 190 (*stream->priv->outstanding_callback) (source_object, res, user_data); 191 g_object_unref (stream); 192 } 193 194 /** 195 * g_file_output_stream_query_info_async: 196 * @stream: a #GFileOutputStream. 197 * @attributes: a file attribute query string. 198 * @io_priority: the <link linkend="gio-GIOScheduler">I/O priority</link> 199 * of the request. 200 * @cancellable: optional #GCancellable object, %NULL to ignore. 201 * @callback: callback to call when the request is satisfied 202 * @user_data: the data to pass to callback function 203 * 204 * Asynchronously queries the @stream for a #GFileInfo. When completed, 205 * @callback will be called with a #GAsyncResult which can be used to 206 * finish the operation with g_file_output_stream_query_info_finish(). 207 * 208 * For the synchronous version of this function, see 209 * g_file_output_stream_query_info(). 210 * 211 **/ 212 void 213 g_file_output_stream_query_info_async (GFileOutputStream *stream, 214 const char *attributes, 215 int io_priority, 216 GCancellable *cancellable, 217 GAsyncReadyCallback callback, 218 gpointer user_data) 219 { 220 GFileOutputStreamClass *klass; 221 GOutputStream *output_stream; 222 GError *error = NULL; 223 224 g_return_if_fail (G_IS_FILE_OUTPUT_STREAM (stream)); 225 226 output_stream = G_OUTPUT_STREAM (stream); 227 228 if (!g_output_stream_set_pending (output_stream, &error)) 229 { 230 g_simple_async_report_gerror_in_idle (G_OBJECT (stream), 231 callback, 232 user_data, 233 error); 234 g_error_free (error); 235 return; 236 } 237 238 klass = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 239 240 stream->priv->outstanding_callback = callback; 241 g_object_ref (stream); 242 klass->query_info_async (stream, attributes, io_priority, cancellable, 243 async_ready_callback_wrapper, user_data); 244 } 245 246 /** 247 * g_file_output_stream_query_info_finish: 248 * @stream: a #GFileOutputStream. 249 * @result: a #GAsyncResult. 250 * @error: a #GError, %NULL to ignore. 251 * 252 * Finalizes the asynchronous query started 253 * by g_file_output_stream_query_info_async(). 254 * 255 * Returns: A #GFileInfo for the finished query. 256 **/ 257 GFileInfo * 258 g_file_output_stream_query_info_finish (GFileOutputStream *stream, 259 GAsyncResult *result, 260 GError **error) 261 { 262 GSimpleAsyncResult *simple; 263 GFileOutputStreamClass *class; 264 265 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL); 266 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); 267 268 if (G_IS_SIMPLE_ASYNC_RESULT (result)) 269 { 270 simple = G_SIMPLE_ASYNC_RESULT (result); 271 if (g_simple_async_result_propagate_error (simple, error)) 272 return NULL; 273 } 274 275 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 276 return class->query_info_finish (stream, result, error); 277 } 278 279 /** 280 * g_file_output_stream_get_etag: 281 * @stream: a #GFileOutputStream. 282 * 283 * Gets the entity tag for the file when it has been written. 284 * This must be called after the stream has been written 285 * and closed, as the etag can change while writing. 286 * 287 * Returns: the entity tag for the stream. 288 **/ 289 char * 290 g_file_output_stream_get_etag (GFileOutputStream *stream) 291 { 292 GFileOutputStreamClass *class; 293 GOutputStream *output_stream; 294 char *etag; 295 296 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL); 297 298 output_stream = G_OUTPUT_STREAM (stream); 299 300 if (!g_output_stream_is_closed (output_stream)) 301 { 302 g_warning ("stream is not closed yet, can't get etag"); 303 return NULL; 304 } 305 306 etag = NULL; 307 308 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 309 if (class->get_etag) 310 etag = class->get_etag (stream); 311 312 return etag; 313 } 314 315 static goffset 316 g_file_output_stream_tell (GFileOutputStream *stream) 317 { 318 GFileOutputStreamClass *class; 319 goffset offset; 320 321 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), 0); 322 323 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 324 325 offset = 0; 326 if (class->tell) 327 offset = class->tell (stream); 328 329 return offset; 330 } 331 332 static goffset 333 g_file_output_stream_seekable_tell (GSeekable *seekable) 334 { 335 return g_file_output_stream_tell (G_FILE_OUTPUT_STREAM (seekable)); 336 } 337 338 static gboolean 339 g_file_output_stream_can_seek (GFileOutputStream *stream) 340 { 341 GFileOutputStreamClass *class; 342 gboolean can_seek; 343 344 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE); 345 346 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 347 348 can_seek = FALSE; 349 if (class->seek) 350 { 351 can_seek = TRUE; 352 if (class->can_seek) 353 can_seek = class->can_seek (stream); 354 } 355 356 return can_seek; 357 } 358 359 static gboolean 360 g_file_output_stream_seekable_can_seek (GSeekable *seekable) 361 { 362 return g_file_output_stream_can_seek (G_FILE_OUTPUT_STREAM (seekable)); 363 } 364 365 static gboolean 366 g_file_output_stream_seek (GFileOutputStream *stream, 367 goffset offset, 368 GSeekType type, 369 GCancellable *cancellable, 370 GError **error) 371 { 372 GFileOutputStreamClass *class; 373 GOutputStream *output_stream; 374 gboolean res; 375 376 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE); 377 378 output_stream = G_OUTPUT_STREAM (stream); 379 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 380 381 if (!class->seek) 382 { 383 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 384 _("Seek not supported on stream")); 385 return FALSE; 386 } 387 388 if (!g_output_stream_set_pending (output_stream, error)) 389 return FALSE; 390 391 if (cancellable) 392 g_cancellable_push_current (cancellable); 393 394 res = class->seek (stream, offset, type, cancellable, error); 395 396 if (cancellable) 397 g_cancellable_pop_current (cancellable); 398 399 g_output_stream_clear_pending (output_stream); 400 401 return res; 402 } 403 404 static gboolean 405 g_file_output_stream_seekable_seek (GSeekable *seekable, 406 goffset offset, 407 GSeekType type, 408 GCancellable *cancellable, 409 GError **error) 410 { 411 return g_file_output_stream_seek (G_FILE_OUTPUT_STREAM (seekable), 412 offset, type, cancellable, error); 413 } 414 415 static gboolean 416 g_file_output_stream_can_truncate (GFileOutputStream *stream) 417 { 418 GFileOutputStreamClass *class; 419 gboolean can_truncate; 420 421 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE); 422 423 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 424 425 can_truncate = FALSE; 426 if (class->truncate_fn) 427 { 428 can_truncate = TRUE; 429 if (class->can_truncate) 430 can_truncate = class->can_truncate (stream); 431 } 432 433 return can_truncate; 434 } 435 436 static gboolean 437 g_file_output_stream_seekable_can_truncate (GSeekable *seekable) 438 { 439 return g_file_output_stream_can_truncate (G_FILE_OUTPUT_STREAM (seekable)); 440 } 441 442 static gboolean 443 g_file_output_stream_truncate (GFileOutputStream *stream, 444 goffset size, 445 GCancellable *cancellable, 446 GError **error) 447 { 448 GFileOutputStreamClass *class; 449 GOutputStream *output_stream; 450 gboolean res; 451 452 g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE); 453 454 output_stream = G_OUTPUT_STREAM (stream); 455 class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream); 456 457 if (!class->truncate_fn) 458 { 459 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 460 _("Truncate not supported on stream")); 461 return FALSE; 462 } 463 464 if (!g_output_stream_set_pending (output_stream, error)) 465 return FALSE; 466 467 if (cancellable) 468 g_cancellable_push_current (cancellable); 469 470 res = class->truncate_fn (stream, size, cancellable, error); 471 472 if (cancellable) 473 g_cancellable_pop_current (cancellable); 474 475 g_output_stream_clear_pending (output_stream); 476 477 return res; 478 } 479 480 static gboolean 481 g_file_output_stream_seekable_truncate (GSeekable *seekable, 482 goffset size, 483 GCancellable *cancellable, 484 GError **error) 485 { 486 return g_file_output_stream_truncate (G_FILE_OUTPUT_STREAM (seekable), 487 size, cancellable, error); 488 } 489 /******************************************** 490 * Default implementation of async ops * 491 ********************************************/ 492 493 typedef struct { 494 char *attributes; 495 GFileInfo *info; 496 } QueryInfoAsyncData; 497 498 static void 499 query_info_data_free (QueryInfoAsyncData *data) 500 { 501 if (data->info) 502 g_object_unref (data->info); 503 g_free (data->attributes); 504 g_free (data); 505 } 506 507 static void 508 query_info_async_thread (GSimpleAsyncResult *res, 509 GObject *object, 510 GCancellable *cancellable) 511 { 512 GFileOutputStreamClass *class; 513 GError *error = NULL; 514 QueryInfoAsyncData *data; 515 GFileInfo *info; 516 517 data = g_simple_async_result_get_op_res_gpointer (res); 518 519 info = NULL; 520 521 class = G_FILE_OUTPUT_STREAM_GET_CLASS (object); 522 if (class->query_info) 523 info = class->query_info (G_FILE_OUTPUT_STREAM (object), data->attributes, cancellable, &error); 524 else 525 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 526 _("Stream doesn't support query_info")); 527 528 if (info == NULL) 529 { 530 g_simple_async_result_set_from_error (res, error); 531 g_error_free (error); 532 } 533 else 534 data->info = info; 535 } 536 537 static void 538 g_file_output_stream_real_query_info_async (GFileOutputStream *stream, 539 const char *attributes, 540 int io_priority, 541 GCancellable *cancellable, 542 GAsyncReadyCallback callback, 543 gpointer user_data) 544 { 545 GSimpleAsyncResult *res; 546 QueryInfoAsyncData *data; 547 548 data = g_new0 (QueryInfoAsyncData, 1); 549 data->attributes = g_strdup (attributes); 550 551 res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_file_output_stream_real_query_info_async); 552 g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_info_data_free); 553 554 g_simple_async_result_run_in_thread (res, query_info_async_thread, io_priority, cancellable); 555 g_object_unref (res); 556 } 557 558 static GFileInfo * 559 g_file_output_stream_real_query_info_finish (GFileOutputStream *stream, 560 GAsyncResult *res, 561 GError **error) 562 { 563 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); 564 QueryInfoAsyncData *data; 565 566 g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_output_stream_real_query_info_async); 567 568 data = g_simple_async_result_get_op_res_gpointer (simple); 569 if (data->info) 570 return g_object_ref (data->info); 571 572 return NULL; 573 } 574 575 #define __G_FILE_OUTPUT_STREAM_C__ 576 #include "gioaliasdef.c" 577