LCOV - code coverage report
Current view: top level - pkcs11/gkm - gkm-transaction.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.7 % 320 223
Test Date: 2024-04-08 13:24:42 Functions: 93.5 % 31 29

            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              : }
        

Generated by: LCOV version 2.0-1