Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2008 Stefan Walter
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see
18 : * <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "config.h"
22 :
23 : #include "gkm-marshal.h"
24 : #include "gkm-transaction.h"
25 :
26 : #include <glib/gstdio.h>
27 :
28 : #include <errno.h>
29 : #include <fcntl.h>
30 : #include <string.h>
31 : #include <unistd.h>
32 :
33 : #ifndef O_BINARY
34 : # define O_BINARY 0
35 : #endif
36 :
37 : enum {
38 : PROP_0,
39 : PROP_COMPLETED,
40 : PROP_FAILED,
41 : PROP_RESULT
42 : };
43 :
44 : enum {
45 : COMPLETE,
46 : LAST_SIGNAL
47 : };
48 :
49 : static guint signals[LAST_SIGNAL] = { 0 };
50 :
51 : struct _GkmTransaction {
52 : GObject parent;
53 : GList *completes;
54 : gboolean failed;
55 : gboolean completed;
56 : CK_RV result;
57 : };
58 :
59 : typedef struct _Complete {
60 : GObject *object;
61 : GkmTransactionFunc func;
62 : gpointer user_data;
63 : } Complete;
64 :
65 7031 : G_DEFINE_TYPE (GkmTransaction, gkm_transaction, G_TYPE_OBJECT);
66 :
67 : #define MAX_TRIES 100000
68 :
69 : /* -----------------------------------------------------------------------------
70 : * INTERNAL
71 : */
72 :
73 : static gboolean
74 627 : complete_invoke (GkmTransaction *transaction, Complete *complete)
75 : {
76 627 : g_assert (complete);
77 627 : g_assert (complete->func);
78 :
79 627 : return (complete->func) (transaction, complete->object, complete->user_data);
80 : }
81 :
82 : static void
83 627 : complete_destroy (Complete *complete)
84 : {
85 627 : g_assert (complete->func);
86 627 : if (complete->object)
87 535 : g_object_unref (complete->object);
88 627 : g_slice_free (Complete, complete);
89 627 : }
90 :
91 : static gboolean
92 318 : complete_accumulator (GSignalInvocationHint *ihint, GValue *return_accu,
93 : const GValue *handler_return, gpointer data)
94 : {
95 : gboolean result;
96 :
97 : /* If any of them return false, then the result is false */
98 318 : result = g_value_get_boolean (handler_return);
99 318 : if (result == FALSE)
100 0 : g_value_set_boolean (return_accu, FALSE);
101 :
102 : /* Continue signal invocations */
103 318 : return TRUE;
104 : }
105 :
106 : static gboolean
107 20 : complete_new_file (GkmTransaction *self, GObject *unused, gpointer user_data)
108 : {
109 20 : gchar *path = user_data;
110 20 : gboolean ret = TRUE;
111 :
112 20 : if (gkm_transaction_get_failed (self)) {
113 3 : if (g_unlink (path) < 0) {
114 0 : g_warning ("couldn't delete aborted file, data may be lost: %s: %s",
115 : path, g_strerror (errno));
116 0 : ret = FALSE;
117 : }
118 : }
119 :
120 20 : g_free (path);
121 20 : return ret;
122 : }
123 :
124 : static gboolean
125 15 : begin_new_file (GkmTransaction *self, const gchar *filename)
126 : {
127 15 : g_assert (GKM_IS_TRANSACTION (self));
128 15 : g_assert (!gkm_transaction_get_failed (self));
129 15 : g_assert (filename);
130 :
131 15 : gkm_transaction_add (self, NULL, complete_new_file, g_strdup (filename));
132 15 : return TRUE;
133 : }
134 :
135 : static gboolean
136 35 : complete_link_temporary (GkmTransaction *self, GObject *unused, gpointer user_data)
137 : {
138 35 : gchar *path = user_data;
139 35 : gboolean ret = TRUE;
140 : gchar *original;
141 : gchar *ext;
142 :
143 : /* When failed, rename temporary back */
144 35 : if (gkm_transaction_get_failed (self)) {
145 :
146 : /* Figure out the original file name */
147 3 : original = g_strdup (path);
148 3 : ext = strrchr (original, '.');
149 3 : g_return_val_if_fail (ext, FALSE);
150 3 : *ext = '\0';
151 :
152 : /* Now rename us back */
153 3 : if (g_rename (path, original) == -1) {
154 0 : g_warning ("couldn't restore original file, data may be lost: %s: %s",
155 : original, g_strerror (errno));
156 0 : ret = FALSE;
157 : }
158 :
159 3 : g_free (original);
160 :
161 : /* When succeeded, remove temporary */
162 : } else {
163 32 : if (g_unlink (path) == -1) {
164 0 : g_warning ("couldn't delete temporary backup file: %s: %s",
165 : path, g_strerror (errno));
166 0 : ret = TRUE; /* Not actually that bad of a situation */
167 : }
168 : }
169 :
170 35 : g_free (path);
171 35 : return ret;
172 : }
173 :
174 :
175 : /* Copy the file SRCNAME to the file DSTNAME. If DSTNAME already
176 : exists -1 is returned and ERRNO set to EEXIST. Returns 0 on
177 : success. */
178 : static int
179 0 : copy_to_temp_file (const char *dstname, const char *srcname)
180 : {
181 : int dstfd, srcfd;
182 : int nread, nwritten;
183 : int saveerr;
184 : char *bufp;
185 : char buffer[512]; /* If you change this size, please also adjust */
186 : /* test-transaction.c:test_write_large_file. */
187 :
188 : do {
189 0 : srcfd = g_open (srcname, (O_RDONLY | O_BINARY));
190 0 : } while (srcfd == -1 && errno == EINTR);
191 0 : if (srcfd == -1) {
192 0 : saveerr = errno;
193 0 : g_warning ("couldn't open file to make temporary copy from: %s: %s",
194 : srcname, g_strerror (saveerr));
195 0 : errno = saveerr;
196 0 : return -1;
197 : }
198 :
199 : do {
200 0 : dstfd = g_open (dstname,
201 : (O_WRONLY | O_CREAT | O_EXCL | O_BINARY),
202 : (S_IRUSR | S_IWUSR));
203 0 : } while (dstfd == -1 && errno == EINTR);
204 0 : if (dstfd == -1) {
205 0 : saveerr = errno;
206 0 : close (srcfd);
207 0 : errno = saveerr;
208 0 : return -1;
209 : }
210 :
211 0 : while ((nread = read (srcfd, buffer, sizeof buffer))) {
212 0 : if (nread == -1 && errno == EINTR)
213 0 : continue;
214 0 : if (nread == -1) {
215 0 : saveerr = errno;
216 0 : g_warning ("error reading file to make temporary copy from: %s: %s",
217 : srcname, g_strerror (saveerr));
218 0 : goto failure;
219 : }
220 :
221 0 : bufp = buffer;
222 : do {
223 : do {
224 0 : nwritten = write (dstfd, bufp, nread);
225 0 : } while (nwritten == -1 && errno == EINTR);
226 0 : if (nwritten == -1) {
227 0 : saveerr = errno;
228 0 : g_warning ("error wrinting to temporary file: %s: %s",
229 : dstname, g_strerror (saveerr));
230 0 : goto failure;
231 : }
232 0 : g_return_val_if_fail (nwritten <= nread, -1);
233 0 : nread -= nwritten;
234 0 : bufp += nwritten;
235 0 : } while (nread > 0);
236 : }
237 : /* EOF reached. */
238 0 : if (close (dstfd)) {
239 0 : saveerr = errno;
240 0 : g_warning ("error closing temporary file: %s: %s",
241 : dstname, g_strerror (saveerr));
242 0 : goto failure;
243 : }
244 0 : close (srcfd);
245 0 : return 0;
246 :
247 0 : failure:
248 0 : close (dstfd); /* (Doesn't harm if we try a second time.) */
249 0 : if (g_unlink (dstname))
250 0 : g_warning ("couldn't remove temporary file: %s: %s",
251 : dstname, g_strerror (saveerr));
252 0 : close (srcfd);
253 0 : errno = saveerr;
254 0 : return -1;
255 : }
256 :
257 :
258 : static gboolean
259 51 : begin_link_temporary_if_exists (GkmTransaction *self, const gchar *filename, gboolean *exists)
260 : {
261 51 : guint i = 0;
262 :
263 51 : g_assert (GKM_IS_TRANSACTION (self));
264 51 : g_assert (!gkm_transaction_get_failed (self));
265 51 : g_assert (filename);
266 51 : g_assert (exists);
267 :
268 51 : for (i = 0; i < MAX_TRIES; ++i) {
269 : struct stat sb;
270 : unsigned int nlink;
271 51 : int stat_failed = 0;
272 :
273 51 : *exists = TRUE;
274 :
275 : /* Try to link to random temporary file names. We try
276 : * to use a hardlink to create a copy but if that
277 : * fails (i.e. not supported by the FS), we copy the
278 : * entire file. The result should be the same except
279 : * that the file times will change if we need to
280 : * rollback the transaction. */
281 51 : if (stat (filename, &sb)) {
282 16 : stat_failed = 1;
283 : } else {
284 : gchar *result;
285 :
286 35 : result = g_strdup_printf ("%s.temp-%d", filename,
287 : g_random_int_range (0, G_MAXINT));
288 35 : nlink = (unsigned int)sb.st_nlink;
289 : /* The result code of link(2) is not reliable.
290 : * Unless it fails with EEXIST we stat the
291 : * file to check for success. Note that there
292 : * is a race here: If another process adds a
293 : * link to the source file between link and
294 : * stat, the check on the increased link count
295 : * will fail. Fortunately the case for
296 : * hardlinks are not working solves it. */
297 35 : if (link (filename, result) && errno == EEXIST) {
298 : /* This is probably a valid error.
299 : * Let us try another temporary file. */
300 35 : } else if (stat (filename, &sb)) {
301 0 : stat_failed = 1;
302 : } else {
303 35 : if ((sb.st_nlink == nlink + 1)
304 0 : || !copy_to_temp_file (result, filename)) {
305 : /* Either the link worked or
306 : * the copy succeeded. */
307 35 : gkm_transaction_add (self, NULL,
308 : complete_link_temporary,
309 : result);
310 51 : return TRUE;
311 : }
312 : }
313 :
314 0 : g_free (result);
315 : }
316 :
317 16 : if (stat_failed && (errno == ENOENT || errno == ENOTDIR)) {
318 : /* The original file does not exist */
319 16 : *exists = FALSE;
320 16 : return TRUE;
321 : }
322 :
323 : /* If exists, try again, otherwise fail */
324 0 : if (errno != EEXIST) {
325 0 : g_warning ("couldn't create temporary file for: %s: %s",
326 : filename, g_strerror (errno));
327 0 : gkm_transaction_fail (self, CKR_DEVICE_ERROR);
328 0 : return FALSE;
329 : }
330 : }
331 :
332 0 : g_assert_not_reached ();
333 : }
334 :
335 : static gboolean
336 44 : write_sync_close (int fd, const guchar *data, gsize n_data)
337 : {
338 : int res;
339 :
340 44 : if (fd == -1)
341 0 : return FALSE;
342 :
343 88 : while (n_data > 0) {
344 44 : res = write (fd, data, n_data);
345 44 : if (res < 0) {
346 0 : if (errno != EINTR && errno != EAGAIN) {
347 0 : close (fd);
348 0 : return FALSE;
349 : }
350 0 : continue;
351 : }
352 44 : g_assert (res <= n_data);
353 :
354 44 : data += res;
355 44 : n_data -= res;
356 : }
357 :
358 : #ifdef HAVE_FSYNC
359 44 : if (fsync (fd) < 0) {
360 0 : close (fd);
361 0 : return FALSE;
362 : }
363 : #endif
364 :
365 44 : if (close (fd) < 0)
366 0 : return FALSE;
367 :
368 44 : return TRUE;
369 : }
370 :
371 : static gboolean
372 44 : write_to_file (const gchar *filename, const guchar *data, gsize n_data)
373 : {
374 : gchar *dirname;
375 : gchar *template;
376 : gboolean result;
377 :
378 44 : g_assert (filename);
379 :
380 44 : dirname = g_path_get_dirname (filename);
381 44 : template = g_build_filename (dirname, ".temp-XXXXXX", NULL);
382 44 : g_free (dirname);
383 :
384 44 : if (write_sync_close (g_mkstemp (template), data, n_data)) {
385 44 : result = g_rename (template, filename) == 0;
386 : } else {
387 0 : g_unlink (template);
388 0 : result = FALSE;
389 : }
390 :
391 44 : g_free (template);
392 44 : return result;
393 : }
394 :
395 : /* -----------------------------------------------------------------------------
396 : * OBJECT
397 : */
398 :
399 : static gboolean
400 315 : gkm_transaction_real_complete (GkmTransaction *self)
401 : {
402 : GList *l;
403 :
404 315 : g_return_val_if_fail (!self->completed, FALSE);
405 315 : self->completed = TRUE;
406 315 : g_object_notify (G_OBJECT (self), "completed");
407 :
408 942 : for (l = self->completes; l; l = g_list_next (l)) {
409 627 : complete_invoke (self, l->data);
410 627 : complete_destroy (l->data);
411 : }
412 :
413 315 : g_list_free (self->completes);
414 315 : self->completes = NULL;
415 :
416 315 : return TRUE;
417 : }
418 :
419 : static void
420 315 : gkm_transaction_init (GkmTransaction *self)
421 : {
422 :
423 315 : }
424 :
425 : static void
426 316 : gkm_transaction_dispose (GObject *obj)
427 : {
428 316 : GkmTransaction *self = GKM_TRANSACTION (obj);
429 :
430 316 : if (!self->completed)
431 18 : gkm_transaction_complete (self);
432 :
433 316 : G_OBJECT_CLASS (gkm_transaction_parent_class)->dispose (obj);
434 316 : }
435 :
436 : static void
437 315 : gkm_transaction_finalize (GObject *obj)
438 : {
439 315 : GkmTransaction *self = GKM_TRANSACTION (obj);
440 :
441 315 : g_assert (!self->completes);
442 315 : g_assert (self->completed);
443 :
444 315 : G_OBJECT_CLASS (gkm_transaction_parent_class)->finalize (obj);
445 315 : }
446 :
447 : static void
448 0 : gkm_transaction_set_property (GObject *obj, guint prop_id, const GValue *value,
449 : GParamSpec *pspec)
450 : {
451 : switch (prop_id) {
452 : default:
453 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
454 0 : break;
455 : }
456 0 : }
457 :
458 : static void
459 3 : gkm_transaction_get_property (GObject *obj, guint prop_id, GValue *value,
460 : GParamSpec *pspec)
461 : {
462 3 : GkmTransaction *self = GKM_TRANSACTION (obj);
463 :
464 3 : switch (prop_id) {
465 1 : case PROP_COMPLETED:
466 1 : g_value_set_boolean (value, gkm_transaction_get_completed (self));
467 1 : break;
468 1 : case PROP_FAILED:
469 1 : g_value_set_boolean (value, gkm_transaction_get_failed (self));
470 1 : break;
471 1 : case PROP_RESULT:
472 1 : g_value_set_ulong (value, gkm_transaction_get_result (self));
473 1 : break;
474 0 : default:
475 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
476 0 : break;
477 : }
478 3 : }
479 :
480 : static void
481 33 : gkm_transaction_class_init (GkmTransactionClass *klass)
482 : {
483 33 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
484 :
485 33 : gobject_class->dispose = gkm_transaction_dispose;
486 33 : gobject_class->finalize = gkm_transaction_finalize;
487 33 : gobject_class->set_property = gkm_transaction_set_property;
488 33 : gobject_class->get_property = gkm_transaction_get_property;
489 :
490 33 : klass->complete = gkm_transaction_real_complete;
491 :
492 33 : g_object_class_install_property (gobject_class, PROP_COMPLETED,
493 : g_param_spec_boolean ("completed", "Completed", "Whether transaction is complete",
494 : FALSE, G_PARAM_READABLE));
495 :
496 33 : g_object_class_install_property (gobject_class, PROP_FAILED,
497 : g_param_spec_boolean ("failed", "Failed", "Whether transaction failed",
498 : FALSE, G_PARAM_READABLE));
499 :
500 33 : g_object_class_install_property (gobject_class, PROP_RESULT,
501 : g_param_spec_ulong ("result", "Result", "Result code for transaction",
502 : 0, G_MAXULONG, CKR_OK, G_PARAM_READABLE));
503 :
504 33 : signals[COMPLETE] = g_signal_new ("complete", GKM_TYPE_TRANSACTION,
505 : G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GkmTransactionClass, complete),
506 : complete_accumulator, NULL, gkm_marshal_BOOLEAN__VOID,
507 : G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
508 33 : }
509 :
510 : /* -----------------------------------------------------------------------------
511 : * PUBLIC
512 : */
513 :
514 : GkmTransaction*
515 315 : gkm_transaction_new (void)
516 : {
517 315 : return g_object_new (GKM_TYPE_TRANSACTION, NULL);
518 : }
519 :
520 : void
521 627 : gkm_transaction_add (GkmTransaction *self, gpointer object,
522 : GkmTransactionFunc func, gpointer user_data)
523 : {
524 : Complete *complete;
525 :
526 627 : g_return_if_fail (GKM_IS_TRANSACTION (self));
527 627 : g_return_if_fail (func);
528 :
529 627 : complete = g_slice_new0 (Complete);
530 627 : complete->func = func;
531 627 : if (object)
532 535 : complete->object = g_object_ref (object);
533 627 : complete->user_data = user_data;
534 :
535 627 : self->completes = g_list_prepend (self->completes, complete);
536 : }
537 :
538 : void
539 40 : gkm_transaction_fail (GkmTransaction *self, CK_RV result)
540 : {
541 40 : g_return_if_fail (GKM_IS_TRANSACTION (self));
542 40 : g_return_if_fail (!self->completed);
543 40 : g_return_if_fail (result != CKR_OK);
544 40 : g_return_if_fail (!self->failed);
545 :
546 40 : self->failed = TRUE;
547 40 : self->result = result;
548 :
549 40 : g_object_notify (G_OBJECT (self), "failed");
550 40 : g_object_notify (G_OBJECT (self), "result");
551 : }
552 :
553 : void
554 315 : gkm_transaction_complete(GkmTransaction *self)
555 : {
556 315 : gboolean critical = FALSE;
557 :
558 315 : g_return_if_fail (GKM_IS_TRANSACTION (self));
559 315 : g_return_if_fail (!self->completed);
560 315 : g_signal_emit (self, signals[COMPLETE], 0, &critical);
561 315 : g_assert (self->completed);
562 :
563 315 : if (!self->failed && critical) {
564 0 : g_warning ("transaction failed to commit, data may be lost");
565 0 : self->failed = TRUE;
566 0 : self->result = CKR_GENERAL_ERROR;
567 0 : g_object_notify (G_OBJECT (self), "failed");
568 0 : g_object_notify (G_OBJECT (self), "result");
569 : }
570 : }
571 :
572 : gboolean
573 4 : gkm_transaction_get_completed (GkmTransaction *self)
574 : {
575 4 : g_return_val_if_fail (GKM_IS_TRANSACTION (self), FALSE);
576 4 : return self->completed;
577 : }
578 :
579 : gboolean
580 3383 : gkm_transaction_get_failed (GkmTransaction *self)
581 : {
582 3383 : g_return_val_if_fail (GKM_IS_TRANSACTION (self), FALSE);
583 3383 : return self->failed;
584 : }
585 :
586 : CK_RV
587 203 : gkm_transaction_get_result (GkmTransaction *self)
588 : {
589 203 : g_return_val_if_fail (GKM_IS_TRANSACTION (self), FALSE);
590 203 : return self->result;
591 : }
592 :
593 : void
594 44 : gkm_transaction_write_file (GkmTransaction *self, const gchar *filename,
595 : gconstpointer data, gsize n_data)
596 : {
597 : gboolean exists;
598 :
599 44 : g_return_if_fail (GKM_IS_TRANSACTION (self));
600 44 : g_return_if_fail (filename);
601 44 : g_return_if_fail (data);
602 44 : g_return_if_fail (!gkm_transaction_get_failed (self));
603 :
604 44 : if (!begin_link_temporary_if_exists (self, filename, &exists))
605 0 : return;
606 :
607 44 : if (!exists) {
608 15 : if (!begin_new_file (self, filename))
609 0 : return;
610 : }
611 :
612 : /* Put data in the expected place */
613 44 : if (!write_to_file (filename, data, n_data)) {
614 0 : g_warning ("couldn't write to file: %s: %s", filename, g_strerror (errno));
615 0 : gkm_transaction_fail (self, CKR_DEVICE_ERROR);
616 : }
617 : }
618 :
619 : gchar*
620 5 : gkm_transaction_unique_file (GkmTransaction *self, const gchar *directory,
621 : const gchar *basename)
622 : {
623 : gchar *ext;
624 5 : gchar *filename = NULL;
625 5 : gchar *base = NULL;
626 5 : gchar *result = NULL;
627 5 : gint seed = 1;
628 : int fd;
629 :
630 5 : g_return_val_if_fail (GKM_IS_TRANSACTION (self), NULL);
631 5 : g_return_val_if_fail (directory, NULL);
632 5 : g_return_val_if_fail (basename, NULL);
633 5 : g_return_val_if_fail (!gkm_transaction_get_failed (self), NULL);
634 :
635 5 : if (g_mkdir_with_parents (directory, S_IRWXU) < 0) {
636 0 : g_warning ("couldn't create directory: %s: %s", directory, g_strerror (errno));
637 0 : gkm_transaction_fail (self, CKR_DEVICE_ERROR);
638 0 : return NULL;
639 : }
640 :
641 5 : filename = g_build_filename (directory, basename, NULL);
642 :
643 : /* Write a zero byte file */
644 5 : fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
645 5 : if (fd != -1) {
646 3 : result = g_strdup (basename);
647 :
648 : /* Try to find a unique filename */
649 2 : } else if (errno == EEXIST) {
650 2 : base = g_strdup (basename);
651 2 : ext = strrchr (base, '.');
652 2 : if (ext != NULL)
653 1 : *(ext++) = '\0';
654 :
655 : do {
656 2 : g_free (result);
657 2 : result = g_strdup_printf ("%s_%d%s%s", base, seed++,
658 : ext ? "." : "", ext ? ext : "");
659 :
660 2 : g_free (filename);
661 2 : filename = g_build_filename (directory, result, NULL);
662 2 : fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
663 :
664 2 : } while (seed < MAX_TRIES && fd == -1 && errno == EEXIST);
665 : }
666 :
667 : /* Something failed */
668 5 : if (fd == -1){
669 0 : g_warning ("couldn't open file: %s: %s", filename, g_strerror (errno));
670 0 : gkm_transaction_fail (self, CKR_DEVICE_ERROR);
671 :
672 : /* Success, just leave our zero byte file */
673 : } else {
674 5 : gkm_transaction_add (self, NULL, complete_new_file, filename);
675 5 : filename = NULL;
676 5 : close (fd);
677 : }
678 :
679 5 : g_free (filename);
680 5 : g_free (base);
681 :
682 5 : if (gkm_transaction_get_failed (self)) {
683 0 : g_free (result);
684 0 : result = NULL;
685 : }
686 :
687 5 : return result;
688 : }
689 :
690 : void
691 7 : gkm_transaction_remove_file (GkmTransaction *self, const gchar *filename)
692 : {
693 : gboolean exists;
694 :
695 8 : g_return_if_fail (GKM_IS_TRANSACTION (self));
696 7 : g_return_if_fail (filename);
697 7 : g_return_if_fail (!gkm_transaction_get_failed (self));
698 :
699 7 : if (!begin_link_temporary_if_exists (self, filename, &exists))
700 0 : return;
701 :
702 : /* Already gone? Job accomplished */
703 7 : if (!exists)
704 1 : return;
705 :
706 : /* If failure, temporary will automatically be removed */
707 6 : if (g_unlink (filename) < 0) {
708 0 : g_warning ("couldn't remove file: %s: %s", filename, g_strerror (errno));
709 0 : gkm_transaction_fail (self, CKR_DEVICE_ERROR);
710 : }
711 : }
712 :
713 : CK_RV
714 121 : gkm_transaction_complete_and_unref (GkmTransaction *self)
715 : {
716 : CK_RV rv;
717 :
718 121 : g_return_val_if_fail (GKM_IS_TRANSACTION (self), CKR_GENERAL_ERROR);
719 :
720 121 : gkm_transaction_complete (self);
721 121 : rv = gkm_transaction_get_result (self);
722 121 : g_object_unref (self);
723 :
724 121 : return rv;
725 : }
|