Coverage Report

Created: 2026-06-07 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ostree/src/libostree/ostree-repo.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2011 Colin Walters <walters@verbum.org>
3
 * Copyright (C) 2015 Red Hat, Inc.
4
 * Copyright (C) 2022 Igalia S.L.
5
 *
6
 * SPDX-License-Identifier: LGPL-2.0+
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library. If not, see <https://www.gnu.org/licenses/>.
20
 *
21
 * Author: Colin Walters <walters@verbum.org>
22
 */
23
24
#include "config.h"
25
26
#include "libglnx.h"
27
#include "ostree-core.h"
28
#include "otutil.h"
29
#include <gio/gfiledescriptorbased.h>
30
#include <gio/gunixinputstream.h>
31
#include <gio/gunixoutputstream.h>
32
#include <glib-unix.h>
33
#include <glnx-console.h>
34
#include <linux/magic.h>
35
36
#include "ostree-autocleanups.h"
37
#include "ostree-core-private.h"
38
#include "ostree-gpg-verifier.h"
39
#include "ostree-remote-private.h"
40
#include "ostree-repo-file-enumerator.h"
41
#include "ostree-repo-file.h"
42
#include "ostree-repo-private.h"
43
#include "ostree-repo-static-delta-private.h"
44
#include "ostree-sign-private.h"
45
#include "ostree-sysroot-private.h"
46
#include "ot-fs-utils.h"
47
48
#include <glib/gstdio.h>
49
#include <locale.h>
50
#include <sys/file.h>
51
#include <sys/statfs.h>
52
#include <sys/statvfs.h>
53
54
0
#define REPO_LOCK_DISABLED (-2)
55
0
#define REPO_LOCK_BLOCKING (-1)
56
57
/* ABI Size checks for ostree-repo.h, only for LP64 systems;
58
 * https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models
59
 *
60
 * To generate this data, I used `pahole` from gdb. More concretely, `gdb --args
61
 * /usr/bin/ostree`, then `start`, (to ensure debuginfo was loaded), then e.g.
62
 * `$ pahole OstreeRepoTransactionStats`.
63
 */
64
#if __SIZEOF_POINTER__ == 8 && __SIZEOF_LONG__ == 8 && __SIZEOF_INT__ == 4
65
G_STATIC_ASSERT (sizeof (OstreeRepoTransactionStats) == sizeof (int) * 4 + 8 * 5);
66
G_STATIC_ASSERT (sizeof (OstreeRepoImportArchiveOptions)
67
                 == sizeof (int) * 9 + 4 + sizeof (void *) * 8);
68
G_STATIC_ASSERT (sizeof (OstreeRepoExportArchiveOptions)
69
                 == sizeof (int) * 9 + 4 + 8 + sizeof (void *) * 8);
70
G_STATIC_ASSERT (sizeof (OstreeRepoCheckoutAtOptions)
71
                 == sizeof (OstreeRepoCheckoutMode) + sizeof (OstreeRepoCheckoutOverwriteMode)
72
                        + sizeof (int) * 6 + sizeof (int) * 5 + sizeof (int) + sizeof (void *) * 2
73
                        + sizeof (int) * 6 + sizeof (void *) * 7);
74
G_STATIC_ASSERT (sizeof (OstreeRepoCommitTraverseIter)
75
                 == sizeof (int) + sizeof (int) + sizeof (void *) * 10 + 130 + 6); /* 6 byte hole */
76
G_STATIC_ASSERT (sizeof (OstreeRepoPruneOptions)
77
                 == sizeof (OstreeRepoPruneFlags) + 4 + sizeof (void *) + sizeof (int) * 12
78
                        + sizeof (void *) * 7);
79
#endif
80
81
/**
82
 * SECTION:ostree-repo
83
 * @title: OstreeRepo: Content-addressed object store
84
 * @short_description: A git-like storage system for operating system binaries
85
 *
86
 * The #OstreeRepo is like git, a content-addressed object store.
87
 * Unlike git, it records uid, gid, and extended attributes.
88
 *
89
 * There are four possible "modes" for an #OstreeRepo; %OSTREE_REPO_MODE_BARE
90
 * is very simple - content files are represented exactly as they are, and
91
 * checkouts are just hardlinks. %OSTREE_REPO_MODE_BARE_USER is similar, except
92
 * the uid/gids are not set on the files, and checkouts as hardlinks work only
93
 * for user checkouts. %OSTREE_REPO_MODE_BARE_USER_ONLY is the same as
94
 * BARE_USER, but all metadata is not stored, so it can only be used for user
95
 * checkouts. This mode does not require xattrs. A %OSTREE_REPO_MODE_ARCHIVE
96
 * (also known as %OSTREE_REPO_MODE_ARCHIVE_Z2) repository in contrast stores
97
 * content files zlib-compressed. It is suitable for non-root-owned
98
 * repositories that can be served via a static HTTP server.
99
 *
100
 * Creating an #OstreeRepo does not invoke any file I/O, and thus needs
101
 * to be initialized, either from existing contents or as a new
102
 * repository. If you have an existing repo, use ostree_repo_open()
103
 * to load it from disk and check its validity. To initialize a new
104
 * repository in the given filepath, use ostree_repo_create() instead.
105
 *
106
 * To store content in the repo, first start a transaction with
107
 * ostree_repo_prepare_transaction().  Then create a
108
 * #OstreeMutableTree, and apply functions such as
109
 * ostree_repo_write_directory_to_mtree() to traverse a physical
110
 * filesystem and write content, possibly multiple times.
111
 *
112
 * Once the #OstreeMutableTree is complete, write all of its metadata
113
 * with ostree_repo_write_mtree(), and finally create a commit with
114
 * ostree_repo_write_commit().
115
 *
116
 * ## Collection IDs
117
 *
118
 * A collection ID is a globally unique identifier which, if set, is used to
119
 * identify refs from a repository which are mirrored elsewhere, such as in
120
 * mirror repositories or peer to peer networks.
121
 *
122
 * This is separate from the `collection-id` configuration key for a remote, which
123
 * is used to store the collection ID of the repository that remote points to.
124
 *
125
 * The collection ID should only be set on an #OstreeRepo if it is the canonical
126
 * collection for some refs.
127
 *
128
 * A collection ID must be a reverse DNS name, where the domain name is under the
129
 * control of the curator of the collection, so they can demonstrate ownership
130
 * of the collection. The later elements in the reverse DNS name can be used to
131
 * disambiguate between multiple collections from the same curator. For example,
132
 * `org.exampleos.Main` and `org.exampleos.Apps`. For the complete format of
133
 * collection IDs, see ostree_validate_collection_id().
134
 */
135
typedef struct
136
{
137
  GObjectClass parent_class;
138
139
#ifndef OSTREE_DISABLE_GPGME
140
  void (*gpg_verify_result) (OstreeRepo *self, const char *checksum, OstreeGpgVerifyResult *result);
141
#endif
142
} OstreeRepoClass;
143
144
enum
145
{
146
  PROP_0,
147
148
  PROP_PATH,
149
  PROP_REMOTES_CONFIG_DIR,
150
  PROP_SYSROOT_PATH
151
};
152
153
enum
154
{
155
  GPG_VERIFY_RESULT,
156
  LAST_SIGNAL
157
};
158
159
#ifndef OSTREE_DISABLE_GPGME
160
static guint signals[LAST_SIGNAL] = { 0 };
161
#endif
162
163
1.52k
G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
164
1.52k
165
1.52k
#define SYSCONF_REMOTES SHORTENED_SYSCONFDIR "/ostree/remotes.d"
166
167
/* Repository locking
168
 *
169
 * To guard against objects being deleted (e.g., prune) while they're in
170
 * use by another operation that is accessing them (e.g., commit), the
171
 * repository must be locked by concurrent writers.
172
 *
173
 * The repository locking has several important features:
174
 *
175
 * * There are 2 states - shared and exclusive. Multiple users can hold
176
 *   a shared lock concurrently while only one user can hold an
177
 *   exclusive lock.
178
 *
179
 * * The lock can be taken recursively so long as each acquisition is paired
180
 *   with a matching release. The recursion is also latched to the strongest
181
 *   state. Once an exclusive lock has been taken, it will remain exclusive
182
 *   until all exclusive locks have been released.
183
 *
184
 * * It is both multiprocess- and multithread-safe. Threads that share
185
 *   an OstreeRepo use the lock cooperatively while processes and
186
 *   threads using separate OstreeRepo structures will block when
187
 *   acquiring incompatible lock states.
188
 *
189
 * The actual locking is implemented using either open file descriptor
190
 * locks or flock locks. This allows the locking to work with concurrent
191
 * processes or concurrent threads using a separate OstreeRepo. The lock
192
 * file is held on the ".lock" file within the repository.
193
 *
194
 * The intended usage is to take a shared lock when writing objects or
195
 * reading objects in critical sections. Exclusive locks are taken when
196
 * deleting objects.
197
 *
198
 * To allow fine grained locking, the lock state is maintained in shared and
199
 * exclusive counters. Callers then push or pop lock types to increment or
200
 * decrement the counters. When pushing or popping a lock type identical to
201
 * the existing or next state, the lock state is simply updated. Only when
202
 * upgrading or downgrading the lock (changing to/from unlocked, pushing
203
 * exclusive on shared or popping exclusive to shared) are actual locking
204
 * operations performed.
205
 */
206
207
typedef struct
208
{
209
  guint len;
210
  int state;
211
  const char *name;
212
} OstreeRepoLockInfo;
213
214
static const char *
215
lock_state_name (int state)
216
0
{
217
0
  switch (state)
218
0
    {
219
0
    case LOCK_EX:
220
0
      return "exclusive";
221
0
    case LOCK_SH:
222
0
      return "shared";
223
0
    case LOCK_UN:
224
0
      return "unlocked";
225
0
    default:
226
0
      g_assert_not_reached ();
227
0
    }
228
0
}
229
230
static void
231
repo_lock_info (OstreeRepo *self, GMutexLocker *locker, OstreeRepoLockInfo *out_info)
232
0
{
233
0
  g_assert (self != NULL);
234
0
  g_assert (locker != NULL);
235
0
  g_assert (out_info != NULL);
236
237
0
  OstreeRepoLockInfo info;
238
0
  info.len = self->lock.shared + self->lock.exclusive;
239
0
  if (info.len == 0)
240
0
    info.state = LOCK_UN;
241
0
  else if (self->lock.exclusive > 0)
242
0
    info.state = LOCK_EX;
243
0
  else
244
0
    info.state = LOCK_SH;
245
0
  info.name = lock_state_name (info.state);
246
247
0
  *out_info = info;
248
0
}
249
250
/* Wrapper to handle flock vs OFD locking based on GLnxLockFile */
251
static gboolean
252
do_repo_lock (int fd, int flags)
253
0
{
254
0
  int res;
255
256
0
#ifdef F_OFD_SETLK
257
0
  struct flock fl = {
258
0
    .l_type = (flags & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
259
0
    .l_whence = SEEK_SET,
260
0
    .l_start = 0,
261
0
    .l_len = 0,
262
0
  };
263
264
0
  res = TEMP_FAILURE_RETRY (fcntl (fd, (flags & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl));
265
#else
266
  res = -1;
267
  errno = EINVAL;
268
#endif
269
270
  /* Fallback to flock when OFD locks not available */
271
0
  if (res < 0)
272
0
    {
273
0
      if (errno == EINVAL)
274
0
        res = TEMP_FAILURE_RETRY (flock (fd, flags));
275
0
      if (res < 0)
276
0
        return FALSE;
277
0
    }
278
279
0
  return TRUE;
280
0
}
281
282
/* Wrapper to handle flock vs OFD unlocking based on GLnxLockFile */
283
static gboolean
284
do_repo_unlock (int fd, int flags)
285
0
{
286
0
  int res;
287
288
0
#ifdef F_OFD_SETLK
289
0
  struct flock fl = {
290
0
    .l_type = F_UNLCK,
291
0
    .l_whence = SEEK_SET,
292
0
    .l_start = 0,
293
0
    .l_len = 0,
294
0
  };
295
296
0
  res = TEMP_FAILURE_RETRY (fcntl (fd, (flags & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl));
297
#else
298
  res = -1;
299
  errno = EINVAL;
300
#endif
301
302
  /* Fallback to flock when OFD locks not available */
303
0
  if (res < 0)
304
0
    {
305
0
      if (errno == EINVAL)
306
0
        res = TEMP_FAILURE_RETRY (flock (fd, LOCK_UN | flags));
307
0
      if (res < 0)
308
0
        return FALSE;
309
0
    }
310
311
0
  return TRUE;
312
0
}
313
314
static gboolean
315
push_repo_lock (OstreeRepo *self, OstreeRepoLockType lock_type, gboolean blocking, GError **error)
316
0
{
317
0
  int flags = (lock_type == OSTREE_REPO_LOCK_EXCLUSIVE) ? LOCK_EX : LOCK_SH;
318
0
  int next_state = flags;
319
0
  if (!blocking)
320
0
    flags |= LOCK_NB;
321
322
0
  g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock.mutex);
323
324
0
  if (self->lock.fd == -1)
325
0
    {
326
0
      g_debug ("Opening repo lock file");
327
0
      self->lock.fd = TEMP_FAILURE_RETRY (
328
0
          openat (self->repo_dir_fd, ".lock", O_CREAT | O_RDWR | O_CLOEXEC, DEFAULT_REGFILE_MODE));
329
0
      if (self->lock.fd < 0)
330
0
        return glnx_throw_errno_prefix (error, "Opening lock file %s/.lock failed",
331
0
                                        gs_file_get_path_cached (self->repodir));
332
0
    }
333
334
0
  OstreeRepoLockInfo info;
335
0
  repo_lock_info (self, locker, &info);
336
0
  g_debug ("Push lock: state=%s, depth=%u", info.name, info.len);
337
338
0
  guint *counter;
339
0
  if (next_state == LOCK_EX)
340
0
    counter = &(self->lock.exclusive);
341
0
  else
342
0
    counter = &(self->lock.shared);
343
344
  /* Check for overflow */
345
0
  if (*counter == G_MAXUINT)
346
0
    g_error ("Repo lock %s counter would overflow", lock_state_name (next_state));
347
348
0
  if (info.state == LOCK_EX || info.state == next_state)
349
0
    {
350
0
      g_debug ("Repo already locked %s, maintaining state", info.name);
351
0
    }
352
0
  else
353
0
    {
354
      /* We should never upgrade from exclusive to shared */
355
0
      g_assert (!(info.state == LOCK_EX && next_state == LOCK_SH));
356
357
0
      const char *next_state_name = lock_state_name (next_state);
358
0
      g_debug ("Locking repo %s", next_state_name);
359
0
      if (!do_repo_lock (self->lock.fd, flags))
360
0
        return glnx_throw_errno_prefix (error, "Locking repo %s failed", next_state_name);
361
0
    }
362
363
  /* Update state */
364
0
  (*counter)++;
365
366
0
  return TRUE;
367
0
}
368
369
static gboolean
370
pop_repo_lock (OstreeRepo *self, OstreeRepoLockType lock_type, gboolean blocking, GError **error)
371
0
{
372
0
  int flags = blocking ? 0 : LOCK_NB;
373
374
0
  g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock.mutex);
375
0
  if (self->lock.fd == -1)
376
0
    g_error ("Cannot pop repo never locked repo lock");
377
378
0
  OstreeRepoLockInfo info;
379
0
  repo_lock_info (self, locker, &info);
380
0
  g_debug ("Pop lock: state=%s, depth=%u", info.name, info.len);
381
382
0
  if (info.len == 0 || info.state == LOCK_UN)
383
0
    g_error ("Cannot pop already unlocked repo lock");
384
385
0
  int state_to_drop;
386
0
  guint *counter;
387
0
  if (lock_type == OSTREE_REPO_LOCK_EXCLUSIVE)
388
0
    {
389
0
      state_to_drop = LOCK_EX;
390
0
      counter = &(self->lock.exclusive);
391
0
    }
392
0
  else
393
0
    {
394
0
      state_to_drop = LOCK_SH;
395
0
      counter = &(self->lock.shared);
396
0
    }
397
398
  /* Make sure caller specified a valid type to release */
399
0
  if (*counter == 0)
400
0
    g_error ("Repo %s lock pop requested, but none have been taken",
401
0
             lock_state_name (state_to_drop));
402
403
0
  int next_state;
404
0
  if (info.len == 1)
405
0
    {
406
      /* Lock counters will be empty, unlock */
407
0
      next_state = LOCK_UN;
408
0
    }
409
0
  else if (state_to_drop == LOCK_EX)
410
0
    next_state = (self->lock.exclusive > 1) ? LOCK_EX : LOCK_SH;
411
0
  else
412
0
    next_state = (self->lock.exclusive > 0) ? LOCK_EX : LOCK_SH;
413
414
0
  if (next_state == LOCK_UN)
415
0
    {
416
0
      g_debug ("Unlocking repo");
417
0
      if (!do_repo_unlock (self->lock.fd, flags))
418
0
        return glnx_throw_errno_prefix (error, "Unlocking repo failed");
419
0
    }
420
0
  else if (info.state == next_state)
421
0
    {
422
0
      g_debug ("Maintaining lock state as %s", info.name);
423
0
    }
424
0
  else
425
0
    {
426
      /* We should never drop from shared to exclusive */
427
0
      g_assert (next_state == LOCK_SH);
428
0
      g_debug ("Returning lock state to shared");
429
0
      if (!do_repo_lock (self->lock.fd, next_state | flags))
430
0
        return glnx_throw_errno_prefix (error, "Setting repo lock to shared failed");
431
0
    }
432
433
  /* Update state */
434
0
  (*counter)--;
435
436
0
  return TRUE;
437
0
}
438
static gboolean reload_config_inner (OstreeRepo *self, GCancellable *cancellable, GError **error);
439
440
/**
441
 * ostree_repo_lock_push:
442
 * @self: a #OstreeRepo
443
 * @lock_type: the type of lock to acquire
444
 * @cancellable: a #GCancellable
445
 * @error: a #GError
446
 *
447
 * Takes a lock on the repository and adds it to the lock state. If @lock_type
448
 * is %OSTREE_REPO_LOCK_SHARED, a shared lock is taken. If @lock_type is
449
 * %OSTREE_REPO_LOCK_EXCLUSIVE, an exclusive lock is taken. The actual lock
450
 * state is only changed when locking a previously unlocked repository or
451
 * upgrading the lock from shared to exclusive. If the requested lock type is
452
 * unchanged or would represent a downgrade (exclusive to shared), the lock
453
 * state is not changed.
454
 *
455
 * ostree_repo_lock_push() waits for the lock depending on the repository's
456
 * lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is
457
 * attempted. Otherwise, the lock is taken non-blocking and
458
 * ostree_repo_lock_push() will sleep synchronously up to lock-timeout-secs seconds
459
 * attempting to acquire the lock. If the lock cannot be acquired within the
460
 * timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned.
461
 *
462
 * If @self is not writable by the user, then no locking is attempted and
463
 * %TRUE is returned.
464
 *
465
 * Returns: %TRUE on success, otherwise %FALSE with @error set
466
 * Since: 2021.3
467
 */
468
gboolean
469
ostree_repo_lock_push (OstreeRepo *self, OstreeRepoLockType lock_type, GCancellable *cancellable,
470
                       GError **error)
471
0
{
472
0
  g_return_val_if_fail (self != NULL, FALSE);
473
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
474
0
  g_return_val_if_fail (self->inited, FALSE);
475
0
  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
476
0
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
477
478
0
  if (!self->writable)
479
0
    return TRUE;
480
481
0
  g_assert (self->lock_timeout_seconds >= REPO_LOCK_DISABLED);
482
0
  if (self->lock_timeout_seconds == REPO_LOCK_DISABLED)
483
0
    return TRUE; /* No locking */
484
0
  else if (self->lock_timeout_seconds == REPO_LOCK_BLOCKING)
485
0
    {
486
0
      g_debug ("Pushing lock blocking");
487
0
      return push_repo_lock (self, lock_type, TRUE, error);
488
0
    }
489
0
  else
490
0
    {
491
      /* Convert to unsigned to guard against negative values */
492
0
      guint lock_timeout_seconds = self->lock_timeout_seconds;
493
0
      guint waited = 0;
494
0
      g_debug ("Pushing lock non-blocking with timeout %u", lock_timeout_seconds);
495
0
      for (;;)
496
0
        {
497
0
          if (g_cancellable_set_error_if_cancelled (cancellable, error))
498
0
            return FALSE;
499
500
0
          g_autoptr (GError) local_error = NULL;
501
0
          if (push_repo_lock (self, lock_type, FALSE, &local_error))
502
0
            return TRUE;
503
504
0
          if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
505
0
            {
506
0
              g_propagate_error (error, g_steal_pointer (&local_error));
507
0
              return FALSE;
508
0
            }
509
510
0
          if (waited >= lock_timeout_seconds)
511
0
            {
512
0
              g_debug ("Push lock: Could not acquire lock within %u seconds", lock_timeout_seconds);
513
0
              g_propagate_error (error, g_steal_pointer (&local_error));
514
0
              return FALSE;
515
0
            }
516
517
          /* Sleep 1 second and try again */
518
0
          if (waited % 60 == 0)
519
0
            {
520
0
              guint remaining = lock_timeout_seconds - waited;
521
0
              g_debug ("Push lock: Waiting %u more second%s to acquire lock", remaining,
522
0
                       (remaining == 1) ? "" : "s");
523
0
            }
524
0
          waited++;
525
0
          sleep (1);
526
0
        }
527
0
    }
528
0
}
529
530
/**
531
 * ostree_repo_lock_pop:
532
 * @self: a #OstreeRepo
533
 * @lock_type: the type of lock to release
534
 * @cancellable: a #GCancellable
535
 * @error: a #GError
536
 *
537
 * Release a lock of type @lock_type from the lock state. If the lock state
538
 * becomes empty, the repository is unlocked. Otherwise, the lock state only
539
 * changes when transitioning from an exclusive lock back to a shared lock. The
540
 * requested @lock_type must be the same type that was requested in the call to
541
 * ostree_repo_lock_push(). It is a programmer error if these do not match and
542
 * the program may abort if the lock would reach an invalid state.
543
 *
544
 * ostree_repo_lock_pop() waits for the lock depending on the repository's
545
 * lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is
546
 * attempted. Otherwise, the lock is removed non-blocking and
547
 * ostree_repo_lock_pop() will sleep synchronously up to lock-timeout-secs seconds
548
 * attempting to remove the lock. If the lock cannot be removed within the
549
 * timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned.
550
 *
551
 * If @self is not writable by the user, then no unlocking is attempted and
552
 * %TRUE is returned.
553
 *
554
 * Returns: %TRUE on success, otherwise %FALSE with @error set
555
 * Since: 2021.3
556
 */
557
gboolean
558
ostree_repo_lock_pop (OstreeRepo *self, OstreeRepoLockType lock_type, GCancellable *cancellable,
559
                      GError **error)
560
0
{
561
0
  g_return_val_if_fail (self != NULL, FALSE);
562
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
563
0
  g_return_val_if_fail (self->inited, FALSE);
564
0
  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
565
0
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
566
567
0
  if (!self->writable)
568
0
    return TRUE;
569
570
0
  g_assert (self->lock_timeout_seconds >= REPO_LOCK_DISABLED);
571
0
  if (self->lock_timeout_seconds == REPO_LOCK_DISABLED)
572
0
    return TRUE;
573
0
  else if (self->lock_timeout_seconds == REPO_LOCK_BLOCKING)
574
0
    {
575
0
      g_debug ("Popping lock blocking");
576
0
      return pop_repo_lock (self, lock_type, TRUE, error);
577
0
    }
578
0
  else
579
0
    {
580
      /* Convert to unsigned to guard against negative values */
581
0
      guint lock_timeout_seconds = self->lock_timeout_seconds;
582
0
      guint waited = 0;
583
0
      g_debug ("Popping lock non-blocking with timeout %u", lock_timeout_seconds);
584
0
      for (;;)
585
0
        {
586
0
          if (g_cancellable_set_error_if_cancelled (cancellable, error))
587
0
            return FALSE;
588
589
0
          g_autoptr (GError) local_error = NULL;
590
0
          if (pop_repo_lock (self, lock_type, FALSE, &local_error))
591
0
            return TRUE;
592
593
0
          if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
594
0
            {
595
0
              g_propagate_error (error, g_steal_pointer (&local_error));
596
0
              return FALSE;
597
0
            }
598
599
0
          if (waited >= lock_timeout_seconds)
600
0
            {
601
0
              g_debug ("Pop lock: Could not remove lock within %u seconds", lock_timeout_seconds);
602
0
              g_propagate_error (error, g_steal_pointer (&local_error));
603
0
              return FALSE;
604
0
            }
605
606
          /* Sleep 1 second and try again */
607
0
          if (waited % 60 == 0)
608
0
            {
609
0
              guint remaining = lock_timeout_seconds - waited;
610
0
              g_debug ("Pop lock: Waiting %u more second%s to remove lock", remaining,
611
0
                       (remaining == 1) ? "" : "s");
612
0
            }
613
0
          waited++;
614
0
          sleep (1);
615
0
        }
616
0
    }
617
0
}
618
619
struct OstreeRepoAutoLock
620
{
621
  OstreeRepo *repo;
622
  OstreeRepoLockType lock_type;
623
};
624
625
/**
626
 * ostree_repo_auto_lock_push: (skip)
627
 * @self: a #OstreeRepo
628
 * @lock_type: the type of lock to acquire
629
 * @cancellable: a #GCancellable
630
 * @error: a #GError
631
 *
632
 * Like ostree_repo_lock_push(), but for usage with #OstreeRepoAutoLock. The
633
 * intended usage is to declare the #OstreeRepoAutoLock with g_autoptr() so
634
 * that ostree_repo_auto_lock_cleanup() is called when it goes out of scope.
635
 * This will automatically release the lock if it was acquired successfully.
636
 *
637
 * |[<!-- language="C" -->
638
 * g_autoptr(OstreeRepoAutoLock) lock = NULL;
639
 * lock = ostree_repo_auto_lock_push (repo, lock_type, cancellable, error);
640
 * if (!lock)
641
 *   return FALSE;
642
 * ]|
643
 *
644
 * Returns: @self on success, otherwise %NULL with @error set
645
 * Since: 2021.3
646
 */
647
OstreeRepoAutoLock *
648
ostree_repo_auto_lock_push (OstreeRepo *self, OstreeRepoLockType lock_type,
649
                            GCancellable *cancellable, GError **error)
650
0
{
651
0
  if (!ostree_repo_lock_push (self, lock_type, cancellable, error))
652
0
    return NULL;
653
654
0
  OstreeRepoAutoLock *auto_lock = g_new (OstreeRepoAutoLock, 1);
655
0
  auto_lock->repo = self;
656
0
  auto_lock->lock_type = lock_type;
657
0
  return auto_lock;
658
0
}
659
660
/**
661
 * ostree_repo_auto_lock_cleanup: (skip)
662
 * @lock: a #OstreeRepoAutoLock
663
 *
664
 * A cleanup handler for use with ostree_repo_auto_lock_push(). If @lock is
665
 * not %NULL, ostree_repo_lock_pop() will be called on it. If
666
 * ostree_repo_lock_pop() fails, a critical warning will be emitted.
667
 *
668
 * Since: 2021.3
669
 */
670
void
671
ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *auto_lock)
672
0
{
673
0
  if (auto_lock != NULL)
674
0
    {
675
0
      g_autoptr (GError) error = NULL;
676
0
      int errsv = errno;
677
678
0
      if (!ostree_repo_lock_pop (auto_lock->repo, auto_lock->lock_type, NULL, &error))
679
0
        g_critical ("Cleanup repo lock failed: %s", error->message);
680
681
0
      errno = errsv;
682
683
0
      g_free (auto_lock);
684
0
    }
685
0
}
686
687
/**
688
 * _ostree_repo_auto_transaction_new:
689
 * @repo: (not nullable): an #OsreeRepo object
690
 * @cancellable: Cancellable
691
 * @error: a #GError
692
 *
693
 * Return a guard for a transaction in @repo.
694
 *
695
 * Do not call this function outside the OstreeRepo transaction implementation.
696
 * Use _ostree_repo_auto_transaction_start() instead.
697
 *
698
 * Returns: (transfer full): an #OstreeRepoAutoTransaction guard on success,
699
 * %NULL otherwise.
700
 */
701
OstreeRepoAutoTransaction *
702
_ostree_repo_auto_transaction_new (OstreeRepo *repo)
703
0
{
704
0
  g_assert (repo != NULL);
705
706
0
  OstreeRepoAutoTransaction *txn = g_malloc (sizeof (OstreeRepoAutoTransaction));
707
0
  txn->atomic_refcount = 1;
708
0
  txn->repo = g_object_ref (repo);
709
710
0
  return g_steal_pointer (&txn);
711
0
}
712
713
/**
714
 * _ostree_repo_auto_transaction_start:
715
 * @repo: (not nullable): an #OsreeRepo object
716
 * @cancellable: Cancellable
717
 * @error: a #GError
718
 *
719
 * Start a transaction and return a guard for it.
720
 *
721
 * Returns: (transfer full): an #OsreeRepoAutoTransaction guard on success,
722
 * %NULL otherwise.
723
 */
724
OstreeRepoAutoTransaction *
725
_ostree_repo_auto_transaction_start (OstreeRepo *repo, GCancellable *cancellable, GError **error)
726
0
{
727
0
  g_assert (repo != NULL);
728
729
0
  if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
730
0
    return NULL;
731
732
0
  return _ostree_repo_auto_transaction_new (repo);
733
0
}
734
735
/**
736
 * _ostree_repo_auto_transaction_abort:
737
 * @txn: (not nullable): an #OsreeRepoAutoTransaction guard
738
 * @cancellable: Cancellable
739
 * @error: a #GError
740
 *
741
 * Abort a transaction, marking the related guard as completed.
742
 *
743
 * Returns: %TRUE on successful commit, %FALSE otherwise.
744
 */
745
gboolean
746
_ostree_repo_auto_transaction_abort (OstreeRepoAutoTransaction *txn, GCancellable *cancellable,
747
                                     GError **error)
748
0
{
749
0
  g_assert (txn != NULL);
750
751
0
  if (txn->repo == NULL)
752
0
    {
753
0
      return glnx_throw (error, "transaction already completed");
754
0
    }
755
756
0
  if (!ostree_repo_abort_transaction (txn->repo, cancellable, error))
757
0
    return FALSE;
758
759
0
  g_clear_object (&txn->repo);
760
761
0
  return TRUE;
762
0
}
763
764
/**
765
 * _ostree_repo_auto_transaction_commit:
766
 * @txn: (not nullable): an #OsreeRepoAutoTransaction guard
767
 * @out_stats: (out) (allow-none): transaction result statistics
768
 * @cancellable: Cancellable
769
 * @error: a #GError
770
 *
771
 * Commit a transaction, marking the related guard as completed.
772
 *
773
 * Returns: %TRUE on successful aborting, %FALSE otherwise.
774
 */
775
gboolean
776
_ostree_repo_auto_transaction_commit (OstreeRepoAutoTransaction *txn,
777
                                      OstreeRepoTransactionStats *out_stats,
778
                                      GCancellable *cancellable, GError **error)
779
0
{
780
0
  g_assert (txn != NULL);
781
782
0
  if (txn->repo == NULL)
783
0
    {
784
0
      return glnx_throw (error, "transaction already completed");
785
0
    }
786
787
0
  if (!ostree_repo_commit_transaction (txn->repo, out_stats, cancellable, error))
788
0
    return FALSE;
789
790
0
  g_clear_object (&txn->repo);
791
792
0
  return TRUE;
793
0
}
794
795
/**
796
 * _ostree_repo_auto_transaction_ref:
797
 * @txn: (not nullable): an #OsreeRepoAutoTransaction guard
798
 *
799
 * Return a new reference to the transaction guard.
800
 *
801
 * Returns: (transfer full) (not nullable): new transaction guard reference.
802
 */
803
OstreeRepoAutoTransaction *
804
_ostree_repo_auto_transaction_ref (OstreeRepoAutoTransaction *txn)
805
0
{
806
0
  g_assert (txn != NULL);
807
808
0
  gint refcount = g_atomic_int_add (&txn->atomic_refcount, 1);
809
0
  g_assert (refcount > 1);
810
811
0
  return txn;
812
0
}
813
814
/**
815
 * _ostree_repo_auto_transaction_unref:
816
 * @txn: (transfer full): an #OsreeRepoAutoTransaction guard
817
 *
818
 * Unreference a transaction guard. When the last reference is gone,
819
 * if the transaction has not yet been completed, it gets aborted.
820
 */
821
void
822
_ostree_repo_auto_transaction_unref (OstreeRepoAutoTransaction *txn)
823
0
{
824
0
  if (txn == NULL)
825
0
    return;
826
827
0
  if (!g_atomic_int_dec_and_test (&txn->atomic_refcount))
828
0
    return;
829
830
  // Auto-abort only if transaction has not already been aborted/committed.
831
0
  if (txn->repo != NULL)
832
0
    {
833
0
      g_autoptr (GError) error = NULL;
834
0
      if (!ostree_repo_abort_transaction (txn->repo, NULL, &error))
835
0
        g_warning ("Failed to auto-cleanup OSTree transaction: %s", error->message);
836
837
0
      g_clear_object (&txn->repo);
838
0
    }
839
840
0
  g_free (txn);
841
0
  return;
842
0
}
843
844
0
G_DEFINE_BOXED_TYPE (OstreeRepoAutoTransaction, _ostree_repo_auto_transaction,
845
0
                     _ostree_repo_auto_transaction_ref, _ostree_repo_auto_transaction_unref);
846
0
847
0
static GFile *get_remotes_d_dir (OstreeRepo *self, GFile *sysroot);
848
0
849
0
OstreeRemote *
850
0
_ostree_repo_get_remote (OstreeRepo *self, const char *name, GError **error)
851
7
{
852
7
  OstreeRemote *remote = NULL;
853
854
7
  g_return_val_if_fail (name != NULL, NULL);
855
856
7
  g_mutex_lock (&self->remotes_lock);
857
858
7
  remote = g_hash_table_lookup (self->remotes, name);
859
860
7
  if (remote != NULL)
861
0
    ostree_remote_ref (remote);
862
7
  else
863
7
    g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Remote \"%s\" not found", name);
864
865
7
  g_mutex_unlock (&self->remotes_lock);
866
867
7
  return remote;
868
7
}
869
870
OstreeRemote *
871
_ostree_repo_get_remote_inherited (OstreeRepo *self, const char *name, GError **error)
872
0
{
873
0
  g_autoptr (OstreeRemote) remote = NULL;
874
0
  g_autoptr (GError) temp_error = NULL;
875
876
0
  remote = _ostree_repo_get_remote (self, name, &temp_error);
877
0
  if (remote == NULL)
878
0
    {
879
0
      if (self->parent_repo != NULL)
880
0
        return _ostree_repo_get_remote_inherited (self->parent_repo, name, error);
881
882
0
      g_propagate_error (error, g_steal_pointer (&temp_error));
883
0
      return NULL;
884
0
    }
885
886
0
  return g_steal_pointer (&remote);
887
0
}
888
889
gboolean
890
_ostree_repo_add_remote (OstreeRepo *self, OstreeRemote *remote)
891
0
{
892
0
  gboolean already_existed;
893
894
0
  g_return_val_if_fail (self != NULL, FALSE);
895
0
  g_return_val_if_fail (remote != NULL, FALSE);
896
0
  g_return_val_if_fail (remote->name != NULL, FALSE);
897
898
0
  g_mutex_lock (&self->remotes_lock);
899
900
0
  already_existed = !g_hash_table_replace (self->remotes, remote->name, ostree_remote_ref (remote));
901
902
0
  g_mutex_unlock (&self->remotes_lock);
903
904
0
  return already_existed;
905
0
}
906
907
gboolean
908
_ostree_repo_remove_remote (OstreeRepo *self, OstreeRemote *remote)
909
0
{
910
0
  gboolean removed;
911
912
0
  g_return_val_if_fail (self != NULL, FALSE);
913
0
  g_return_val_if_fail (remote != NULL, FALSE);
914
0
  g_return_val_if_fail (remote->name != NULL, FALSE);
915
916
0
  g_mutex_lock (&self->remotes_lock);
917
918
0
  removed = g_hash_table_remove (self->remotes, remote->name);
919
920
0
  g_mutex_unlock (&self->remotes_lock);
921
922
0
  return removed;
923
0
}
924
925
gboolean
926
_ostree_repo_remote_name_is_file (const char *remote_name)
927
0
{
928
0
  return g_str_has_prefix (remote_name, "file://");
929
0
}
930
931
/**
932
 * ostree_repo_get_remote_option:
933
 * @self: A OstreeRepo
934
 * @remote_name: Name
935
 * @option_name: Option
936
 * @default_value: (nullable): Value returned if @option_name is not present
937
 * @out_value: (out) (nullable): Return location for value
938
 * @error: Error
939
 *
940
 * OSTree remotes are represented by keyfile groups, formatted like:
941
 * `[remote "remotename"]`. This function returns a value named @option_name
942
 * underneath that group, or @default_value if the remote exists but not the
943
 * option name.  If an error is returned, @out_value will be set to %NULL.
944
 *
945
 * Returns: %TRUE on success, otherwise %FALSE with @error set
946
 *
947
 * Since: 2016.5
948
 */
949
gboolean
950
ostree_repo_get_remote_option (OstreeRepo *self, const char *remote_name, const char *option_name,
951
                               const char *default_value, char **out_value, GError **error)
952
0
{
953
0
  g_autoptr (OstreeRemote) remote = NULL;
954
0
  gboolean ret = FALSE;
955
0
  g_autoptr (GError) temp_error = NULL;
956
0
  g_autofree char *value = NULL;
957
958
0
  if (_ostree_repo_remote_name_is_file (remote_name))
959
0
    {
960
0
      *out_value = g_strdup (default_value);
961
0
      return TRUE;
962
0
    }
963
964
0
  remote = _ostree_repo_get_remote (self, remote_name, &temp_error);
965
0
  if (remote != NULL)
966
0
    {
967
0
      value = g_key_file_get_string (remote->options, remote->group, option_name, &temp_error);
968
0
      if (value == NULL)
969
0
        {
970
0
          if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
971
0
            {
972
              /* Note: We ignore errors on the parent because the parent config may not
973
                 specify this remote, causing a "remote not found" error, but we found
974
                 the remote at some point, so we need to instead return the default */
975
0
              if (self->parent_repo != NULL
976
0
                  && ostree_repo_get_remote_option (self->parent_repo, remote_name, option_name,
977
0
                                                    default_value, out_value, NULL))
978
0
                return TRUE;
979
980
0
              value = g_strdup (default_value);
981
0
              ret = TRUE;
982
0
            }
983
0
          else
984
0
            g_propagate_error (error, g_steal_pointer (&temp_error));
985
0
        }
986
0
      else
987
0
        ret = TRUE;
988
0
    }
989
0
  else if (self->parent_repo != NULL)
990
0
    return ostree_repo_get_remote_option (self->parent_repo, remote_name, option_name,
991
0
                                          default_value, out_value, error);
992
0
  else
993
0
    g_propagate_error (error, g_steal_pointer (&temp_error));
994
995
0
  *out_value = g_steal_pointer (&value);
996
0
  return ret;
997
0
}
998
999
/**
1000
 * ostree_repo_get_remote_list_option:
1001
 * @self: A OstreeRepo
1002
 * @remote_name: Name
1003
 * @option_name: Option
1004
 * @out_value: (out) (array zero-terminated=1): location to store the list
1005
 *            of strings. The list should be freed with
1006
 *            g_strfreev().
1007
 * @error: Error
1008
 *
1009
 * OSTree remotes are represented by keyfile groups, formatted like:
1010
 * `[remote "remotename"]`. This function returns a value named @option_name
1011
 * underneath that group, and returns it as a zero terminated array of strings.
1012
 * If the option is not set, or if an error is returned, @out_value will be set
1013
 * to %NULL.
1014
 *
1015
 * Returns: %TRUE on success, otherwise %FALSE with @error set
1016
 *
1017
 * Since: 2016.5
1018
 */
1019
gboolean
1020
ostree_repo_get_remote_list_option (OstreeRepo *self, const char *remote_name,
1021
                                    const char *option_name, char ***out_value, GError **error)
1022
0
{
1023
0
  g_autoptr (OstreeRemote) remote = NULL;
1024
0
  gboolean ret = FALSE;
1025
0
  g_autoptr (GError) temp_error = NULL;
1026
0
  g_auto (GStrv) value = NULL;
1027
1028
0
  if (_ostree_repo_remote_name_is_file (remote_name))
1029
0
    {
1030
0
      *out_value = NULL;
1031
0
      return TRUE;
1032
0
    }
1033
1034
0
  remote = _ostree_repo_get_remote (self, remote_name, &temp_error);
1035
0
  if (remote != NULL)
1036
0
    {
1037
0
      value = g_key_file_get_string_list (remote->options, remote->group, option_name, NULL,
1038
0
                                          &temp_error);
1039
1040
      /* Default value if key not found is always NULL. */
1041
0
      if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
1042
0
        {
1043
          /* Note: We ignore errors on the parent because the parent config may not
1044
             specify this remote, causing a "remote not found" error, but we found
1045
             the remote at some point, so we need to instead return the default */
1046
0
          if (self->parent_repo != NULL
1047
0
              && ostree_repo_get_remote_list_option (self->parent_repo, remote_name, option_name,
1048
0
                                                     out_value, NULL))
1049
0
            return TRUE;
1050
1051
0
          ret = TRUE;
1052
0
        }
1053
0
      else if (temp_error)
1054
0
        g_propagate_error (error, g_steal_pointer (&temp_error));
1055
0
      else
1056
0
        ret = TRUE;
1057
0
    }
1058
0
  else if (self->parent_repo != NULL)
1059
0
    return ostree_repo_get_remote_list_option (self->parent_repo, remote_name, option_name,
1060
0
                                               out_value, error);
1061
0
  else
1062
0
    g_propagate_error (error, g_steal_pointer (&temp_error));
1063
1064
0
  *out_value = g_steal_pointer (&value);
1065
0
  return ret;
1066
0
}
1067
1068
/**
1069
 * ostree_repo_get_remote_boolean_option:
1070
 * @self: A OstreeRepo
1071
 * @remote_name: Name
1072
 * @option_name: Option
1073
 * @default_value: Value returned if @option_name is not present
1074
 * @out_value: (out) : location to store the result.
1075
 * @error: Error
1076
 *
1077
 * OSTree remotes are represented by keyfile groups, formatted like:
1078
 * `[remote "remotename"]`. This function returns a value named @option_name
1079
 * underneath that group, and returns it as a boolean.
1080
 * If the option is not set, @out_value will be set to @default_value. If an
1081
 * error is returned, @out_value will be set to %FALSE.
1082
 *
1083
 * Returns: %TRUE on success, otherwise %FALSE with @error set
1084
 *
1085
 * Since: 2016.5
1086
 */
1087
gboolean
1088
ostree_repo_get_remote_boolean_option (OstreeRepo *self, const char *remote_name,
1089
                                       const char *option_name, gboolean default_value,
1090
                                       gboolean *out_value, GError **error)
1091
0
{
1092
0
  g_autoptr (OstreeRemote) remote = NULL;
1093
0
  g_autoptr (GError) temp_error = NULL;
1094
0
  gboolean ret = FALSE;
1095
0
  gboolean value = FALSE;
1096
1097
0
  if (_ostree_repo_remote_name_is_file (remote_name))
1098
0
    {
1099
0
      *out_value = default_value;
1100
0
      return TRUE;
1101
0
    }
1102
1103
0
  remote = _ostree_repo_get_remote (self, remote_name, &temp_error);
1104
0
  if (remote != NULL)
1105
0
    {
1106
0
      value = g_key_file_get_boolean (remote->options, remote->group, option_name, &temp_error);
1107
0
      if (temp_error != NULL)
1108
0
        {
1109
0
          if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
1110
0
            {
1111
              /* Note: We ignore errors on the parent because the parent config may not
1112
                 specify this remote, causing a "remote not found" error, but we found
1113
                 the remote at some point, so we need to instead return the default */
1114
0
              if (self->parent_repo != NULL
1115
0
                  && ostree_repo_get_remote_boolean_option (
1116
0
                      self->parent_repo, remote_name, option_name, default_value, out_value, NULL))
1117
0
                return TRUE;
1118
1119
0
              value = default_value;
1120
0
              ret = TRUE;
1121
0
            }
1122
0
          else
1123
0
            g_propagate_error (error, g_steal_pointer (&temp_error));
1124
0
        }
1125
0
      else
1126
0
        ret = TRUE;
1127
0
    }
1128
0
  else if (self->parent_repo != NULL)
1129
0
    return ostree_repo_get_remote_boolean_option (self->parent_repo, remote_name, option_name,
1130
0
                                                  default_value, out_value, error);
1131
0
  else
1132
0
    g_propagate_error (error, g_steal_pointer (&temp_error));
1133
1134
0
  *out_value = value;
1135
0
  return ret;
1136
0
}
1137
1138
static void
1139
ostree_repo_finalize (GObject *object)
1140
139
{
1141
139
  OstreeRepo *self = OSTREE_REPO (object);
1142
1143
139
  g_clear_object (&self->parent_repo);
1144
1145
139
  g_free (self->stagedir_prefix);
1146
139
  g_clear_object (&self->repodir_fdrel);
1147
139
  g_clear_object (&self->repodir);
1148
139
  glnx_close_fd (&self->repo_dir_fd);
1149
139
  glnx_tmpdir_unset (&self->commit_stagedir);
1150
139
  glnx_release_lock_file (&self->commit_stagedir_lock);
1151
139
  glnx_close_fd (&self->tmp_dir_fd);
1152
139
  glnx_close_fd (&self->cache_dir_fd);
1153
139
  glnx_close_fd (&self->objects_dir_fd);
1154
139
  glnx_close_fd (&self->uncompressed_objects_dir_fd);
1155
139
  g_clear_object (&self->sysroot_dir);
1156
139
  g_weak_ref_clear (&self->sysroot);
1157
139
  g_free (self->remotes_config_dir);
1158
1159
139
  if (self->loose_object_devino_hash)
1160
0
    g_hash_table_destroy (self->loose_object_devino_hash);
1161
139
  if (self->updated_uncompressed_dirs)
1162
0
    g_hash_table_destroy (self->updated_uncompressed_dirs);
1163
139
  if (self->config)
1164
139
    g_key_file_free (self->config);
1165
139
  g_clear_pointer (&self->txn.refs, g_hash_table_destroy);
1166
139
  g_clear_pointer (&self->txn.collection_refs, g_hash_table_destroy);
1167
139
  g_clear_error (&self->writable_error);
1168
139
  g_clear_pointer (&self->object_sizes, g_hash_table_unref);
1169
139
  g_clear_pointer (&self->dirmeta_cache, g_hash_table_unref);
1170
139
  g_mutex_clear (&self->cache_lock);
1171
139
  g_mutex_clear (&self->txn_lock);
1172
139
  g_free (self->collection_id);
1173
139
  g_strfreev (self->repo_finders);
1174
139
  g_clear_pointer (&self->bls_append_values, g_hash_table_unref);
1175
1176
139
  g_clear_pointer (&self->remotes, g_hash_table_destroy);
1177
139
  g_mutex_clear (&self->remotes_lock);
1178
1179
139
  glnx_close_fd (&self->lock.fd);
1180
139
  g_mutex_clear (&self->lock.mutex);
1181
1182
139
  G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
1183
139
}
1184
1185
static void
1186
ostree_repo_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1187
417
{
1188
417
  OstreeRepo *self = OSTREE_REPO (object);
1189
1190
417
  switch (prop_id)
1191
417
    {
1192
139
    case PROP_PATH:
1193
139
      self->repodir = g_value_dup_object (value);
1194
139
      break;
1195
139
    case PROP_SYSROOT_PATH:
1196
139
      self->sysroot_dir = g_value_dup_object (value);
1197
139
      break;
1198
139
    case PROP_REMOTES_CONFIG_DIR:
1199
139
      self->remotes_config_dir = g_value_dup_string (value);
1200
139
      break;
1201
0
    default:
1202
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1203
0
      break;
1204
417
    }
1205
417
}
1206
1207
static void
1208
ostree_repo_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1209
0
{
1210
0
  OstreeRepo *self = OSTREE_REPO (object);
1211
1212
0
  switch (prop_id)
1213
0
    {
1214
0
    case PROP_PATH:
1215
0
      g_value_set_object (value, self->repodir);
1216
0
      break;
1217
0
    case PROP_SYSROOT_PATH:
1218
0
      g_value_set_object (value, self->sysroot_dir);
1219
0
      break;
1220
0
    case PROP_REMOTES_CONFIG_DIR:
1221
0
      g_value_set_string (value, self->remotes_config_dir);
1222
0
      break;
1223
0
    default:
1224
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1225
0
      break;
1226
0
    }
1227
0
}
1228
1229
static void
1230
ostree_repo_class_init (OstreeRepoClass *klass)
1231
1
{
1232
1
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
1233
1234
1
  object_class->get_property = ostree_repo_get_property;
1235
1
  object_class->set_property = ostree_repo_set_property;
1236
1
  object_class->finalize = ostree_repo_finalize;
1237
1238
  /**
1239
   * OstreeRepo:path:
1240
   *
1241
   * Path to repository.  Note that if this repository was created
1242
   * via `ostree_repo_new_at()`, this value will refer to a value in
1243
   * the Linux kernel's `/proc/self/fd` directory.  Generally, you
1244
   * should avoid using this property at all; you can gain a reference
1245
   * to the repository's directory fd via `ostree_repo_get_dfd()` and
1246
   * use file-descriptor relative operations.
1247
   */
1248
1
  g_object_class_install_property (
1249
1
      object_class, PROP_PATH,
1250
1
      g_param_spec_object ("path", "Path", "Path", G_TYPE_FILE,
1251
1
                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1252
  /**
1253
   * OstreeRepo:sysroot-path:
1254
   *
1255
   * A system using libostree for the host has a "system" repository; this
1256
   * property will be set for repositories referenced via
1257
   * `ostree_sysroot_repo()` for example.
1258
   *
1259
   * You should avoid using this property; if your code is operating
1260
   * on a system repository, use `OstreeSysroot` and access the repository
1261
   * object via `ostree_sysroot_repo()`.
1262
   */
1263
1
  g_object_class_install_property (
1264
1
      object_class, PROP_SYSROOT_PATH,
1265
1
      g_param_spec_object ("sysroot-path", "", "", G_TYPE_FILE,
1266
1
                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1267
  /**
1268
   * OstreeRepo:remotes-config-dir:
1269
   *
1270
   * Path to directory containing remote definitions.  The default is `NULL`.
1271
   * If a `sysroot-path` property is defined, this value will default to
1272
   * `${sysroot_path}/etc/ostree/remotes.d`.
1273
   *
1274
   * This value will only be used for system repositories.
1275
   */
1276
1
  g_object_class_install_property (
1277
1
      object_class, PROP_REMOTES_CONFIG_DIR,
1278
1
      g_param_spec_string ("remotes-config-dir", "", "", NULL,
1279
1
                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1280
1281
1
#ifndef OSTREE_DISABLE_GPGME
1282
  /**
1283
   * OstreeRepo::gpg-verify-result:
1284
   * @self: an #OstreeRepo
1285
   * @checksum: checksum of the signed object
1286
   * @result: an #OstreeGpgVerifyResult
1287
   *
1288
   * Emitted during a pull operation upon GPG verification (if enabled).
1289
   * Applications can connect to this signal to output the verification
1290
   * results if desired.
1291
   *
1292
   * The signal will be emitted from whichever #GMainContext is the
1293
   * thread-default at the point when ostree_repo_pull_with_options()
1294
   * is called.
1295
   */
1296
1
  signals[GPG_VERIFY_RESULT]
1297
1
      = g_signal_new ("gpg-verify-result", OSTREE_TYPE_REPO, G_SIGNAL_RUN_LAST,
1298
1
                      G_STRUCT_OFFSET (OstreeRepoClass, gpg_verify_result), NULL, NULL, NULL,
1299
1
                      G_TYPE_NONE, 2, G_TYPE_STRING, OSTREE_TYPE_GPG_VERIFY_RESULT);
1300
1
#endif /* OSTREE_DISABLE_GPGME */
1301
1
}
1302
1303
static void
1304
ostree_repo_init (OstreeRepo *self)
1305
139
{
1306
139
  const GDebugKey test_error_keys[] = {
1307
139
    { "pre-commit", OSTREE_REPO_TEST_ERROR_PRE_COMMIT },
1308
139
    { "invalid-cache", OSTREE_REPO_TEST_ERROR_INVALID_CACHE },
1309
139
  };
1310
1311
139
#ifndef OSTREE_DISABLE_GPGME
1312
139
  static gsize gpgme_initialized;
1313
1314
139
  if (g_once_init_enter (&gpgme_initialized))
1315
1
    {
1316
1
      gpgme_check_version (NULL);
1317
1
      gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
1318
1
      g_once_init_leave (&gpgme_initialized, 1);
1319
1
    }
1320
139
#endif
1321
1322
139
  self->test_error_flags = g_parse_debug_string (g_getenv ("OSTREE_REPO_TEST_ERROR"),
1323
139
                                                 test_error_keys, G_N_ELEMENTS (test_error_keys));
1324
1325
139
  g_mutex_init (&self->lock.mutex);
1326
139
  g_mutex_init (&self->cache_lock);
1327
139
  g_mutex_init (&self->txn_lock);
1328
1329
139
  self->remotes = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)NULL,
1330
139
                                         (GDestroyNotify)ostree_remote_unref);
1331
139
  self->bls_append_values = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free,
1332
139
                                                   (GDestroyNotify)g_free);
1333
139
  g_mutex_init (&self->remotes_lock);
1334
1335
139
  self->repo_dir_fd = -1;
1336
139
  self->cache_dir_fd = -1;
1337
139
  self->tmp_dir_fd = -1;
1338
139
  self->objects_dir_fd = -1;
1339
139
  self->uncompressed_objects_dir_fd = -1;
1340
139
  self->lock.fd = -1;
1341
139
  self->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_UNKNOWN;
1342
139
}
1343
1344
/**
1345
 * ostree_repo_new:
1346
 * @path: Path to a repository
1347
 *
1348
 * Returns: (transfer full): An accessor object for an OSTree repository located at @path
1349
 */
1350
OstreeRepo *
1351
ostree_repo_new (GFile *path)
1352
0
{
1353
0
  return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
1354
0
}
1355
1356
static OstreeRepo *
1357
repo_open_at_take_fd (int *dfd, GCancellable *cancellable, GError **error)
1358
139
{
1359
139
  g_autoptr (OstreeRepo) repo = g_object_new (OSTREE_TYPE_REPO, NULL);
1360
139
  repo->repo_dir_fd = g_steal_fd (dfd);
1361
1362
139
  if (!ostree_repo_open (repo, cancellable, error))
1363
0
    return NULL;
1364
139
  return g_steal_pointer (&repo);
1365
139
}
1366
1367
/**
1368
 * ostree_repo_open_at:
1369
 * @dfd: Directory fd
1370
 * @path: Path
1371
 *
1372
 * This combines ostree_repo_new() (but using fd-relative access) with
1373
 * ostree_repo_open().  Use this when you know you should be operating on an
1374
 * already extant repository.  If you want to create one, use ostree_repo_create_at().
1375
 *
1376
 * Returns: (transfer full): An accessor object for an OSTree repository located at @dfd + @path
1377
 *
1378
 * Since: 2017.10
1379
 */
1380
OstreeRepo *
1381
ostree_repo_open_at (int dfd, const char *path, GCancellable *cancellable, GError **error)
1382
0
{
1383
0
  glnx_autofd int repo_dfd = -1;
1384
0
  if (!glnx_opendirat (dfd, path, TRUE, &repo_dfd, error))
1385
0
    return NULL;
1386
1387
0
  return repo_open_at_take_fd (&repo_dfd, cancellable, error);
1388
0
}
1389
1390
static GFile *
1391
get_default_repo_path (GFile *sysroot_path)
1392
0
{
1393
0
  if (sysroot_path == NULL)
1394
0
    sysroot_path = _ostree_get_default_sysroot_path ();
1395
1396
0
  return g_file_resolve_relative_path (sysroot_path, "ostree/repo");
1397
0
}
1398
1399
/**
1400
 * ostree_repo_new_for_sysroot_path:
1401
 * @repo_path: Path to a repository
1402
 * @sysroot_path: Path to the system root
1403
 *
1404
 * Creates a new #OstreeRepo instance, taking the system root path explicitly
1405
 * instead of assuming "/".
1406
 *
1407
 * Returns: (transfer full): An accessor object for the OSTree repository located at @repo_path.
1408
 */
1409
OstreeRepo *
1410
ostree_repo_new_for_sysroot_path (GFile *repo_path, GFile *sysroot_path)
1411
0
{
1412
0
  return g_object_new (OSTREE_TYPE_REPO, "path", repo_path, "sysroot-path", sysroot_path, NULL);
1413
0
}
1414
1415
/**
1416
 * ostree_repo_new_default:
1417
 *
1418
 * If the current working directory appears to be an OSTree
1419
 * repository, create a new #OstreeRepo object for accessing it.
1420
 * Otherwise use the path in the OSTREE_REPO environment variable
1421
 * (if defined) or else the default system repository located at
1422
 * /ostree/repo.
1423
 *
1424
 * Returns: (transfer full): An accessor object for an OSTree repository located at /ostree/repo
1425
 */
1426
OstreeRepo *
1427
ostree_repo_new_default (void)
1428
0
{
1429
0
  if (g_file_test ("objects", G_FILE_TEST_IS_DIR) && g_file_test ("config", G_FILE_TEST_IS_REGULAR))
1430
0
    {
1431
0
      g_autoptr (GFile) cwd = g_file_new_for_path (".");
1432
0
      return ostree_repo_new (cwd);
1433
0
    }
1434
0
  else
1435
0
    {
1436
0
      const char *envvar = g_getenv ("OSTREE_REPO");
1437
0
      g_autoptr (GFile) repo_path = NULL;
1438
1439
0
      if (envvar == NULL || *envvar == '\0')
1440
0
        repo_path = get_default_repo_path (NULL);
1441
0
      else
1442
0
        repo_path = g_file_new_for_path (envvar);
1443
1444
0
      return ostree_repo_new (repo_path);
1445
0
    }
1446
0
}
1447
1448
/**
1449
 * ostree_repo_is_system:
1450
 * @repo: Repository
1451
 *
1452
 * Returns: %TRUE if this repository is the root-owned system global repository
1453
 */
1454
gboolean
1455
ostree_repo_is_system (OstreeRepo *repo)
1456
228
{
1457
228
  g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE);
1458
1459
  /* If we were created via ostree_sysroot_get_repo(), we know the answer is yes
1460
   * without having to compare file paths.
1461
   */
1462
228
  if (repo->sysroot_kind == OSTREE_REPO_SYSROOT_KIND_VIA_SYSROOT
1463
228
      || repo->sysroot_kind == OSTREE_REPO_SYSROOT_KIND_IS_SYSROOT_OSTREE)
1464
0
    return TRUE;
1465
1466
  /* No sysroot_dir set?  Not a system repo then. */
1467
228
  if (!repo->sysroot_dir)
1468
228
    return FALSE;
1469
1470
  /* If we created via ostree_repo_new(), we'll have a repo path.  Compare
1471
   * it to the sysroot path in that case.
1472
   */
1473
0
  if (repo->repodir)
1474
0
    {
1475
0
      g_autoptr (GFile) default_repo_path = get_default_repo_path (repo->sysroot_dir);
1476
0
      return g_file_equal (repo->repodir, default_repo_path);
1477
0
    }
1478
  /* Otherwise, not a system repo */
1479
0
  return FALSE;
1480
0
}
1481
1482
/**
1483
 * ostree_repo_is_writable:
1484
 * @self: Repo
1485
 * @error: a #GError
1486
 *
1487
 * Returns whether the repository is writable by the current user.
1488
 * If the repository is not writable, the @error indicates why.
1489
 *
1490
 * Returns: %TRUE if this repository is writable
1491
 */
1492
gboolean
1493
ostree_repo_is_writable (OstreeRepo *self, GError **error)
1494
0
{
1495
0
  g_assert (self != NULL);
1496
0
  g_assert (self->inited);
1497
1498
0
  g_assert (self->writable == (self->writable_error == NULL));
1499
0
  if (error != NULL && self->writable_error != NULL)
1500
0
    *error = g_error_copy (self->writable_error);
1501
1502
0
  return self->writable;
1503
0
}
1504
1505
/**
1506
 * _ostree_repo_update_mtime:
1507
 * @self: Repo
1508
 * @error: a #GError
1509
 *
1510
 * Bump the mtime of the repository so that programs
1511
 * can detect that the refs have updated.
1512
 */
1513
gboolean
1514
_ostree_repo_update_mtime (OstreeRepo *self, GError **error)
1515
0
{
1516
0
  if (futimens (self->repo_dir_fd, NULL) != 0)
1517
0
    {
1518
0
      glnx_set_prefix_error_from_errno (error, "%s", "futimens");
1519
0
      return FALSE;
1520
0
    }
1521
0
  return TRUE;
1522
0
}
1523
1524
gboolean
1525
_ostree_repo_syncfs (OstreeRepo *self, GError **error)
1526
0
{
1527
1528
0
  if (self->disable_fsync)
1529
0
    return TRUE;
1530
1531
0
  gboolean is_system = ostree_repo_is_system (self);
1532
0
  if (is_system)
1533
0
    ot_journal_print (LOG_INFO, "Starting syncfs for system repo");
1534
0
  guint64 start_msec = g_get_monotonic_time () / 1000;
1535
0
  int repo_dfd = ostree_repo_get_dfd (self);
1536
0
  if (syncfs (repo_dfd) != 0)
1537
0
    return glnx_throw_errno_prefix (error, "syncfs(repo)");
1538
0
  guint64 end_msec = g_get_monotonic_time () / 1000;
1539
0
  if (is_system)
1540
0
    ot_journal_print (LOG_INFO, "Completed syncfs() for system repo in %" G_GUINT64_FORMAT " ms",
1541
0
                      end_msec - start_msec);
1542
0
  return TRUE;
1543
0
}
1544
1545
/**
1546
 * ostree_repo_get_config:
1547
 * @self:
1548
 *
1549
 * Returns: (transfer none): The repository configuration; do not modify
1550
 */
1551
GKeyFile *
1552
ostree_repo_get_config (OstreeRepo *self)
1553
0
{
1554
0
  g_assert (self != NULL);
1555
0
  g_assert (self->inited);
1556
1557
0
  return self->config;
1558
0
}
1559
1560
/**
1561
 * ostree_repo_copy_config:
1562
 * @self:
1563
 *
1564
 * Returns: (transfer full): A newly-allocated copy of the repository config
1565
 */
1566
GKeyFile *
1567
ostree_repo_copy_config (OstreeRepo *self)
1568
139
{
1569
139
  GKeyFile *copy;
1570
139
  char *data;
1571
139
  gsize len;
1572
1573
139
  g_assert (self != NULL);
1574
139
  g_assert (self->inited);
1575
1576
139
  copy = g_key_file_new ();
1577
139
  data = g_key_file_to_data (self->config, &len, NULL);
1578
139
  if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
1579
139
    g_assert_not_reached ();
1580
139
  g_free (data);
1581
139
  return copy;
1582
139
}
1583
1584
/**
1585
 * ostree_repo_write_config:
1586
 * @self: Repo
1587
 * @new_config: Overwrite the config file with this data
1588
 * @error: a #GError
1589
 *
1590
 * Save @new_config in place of this repository's config file.
1591
 *
1592
 * Note: This will not validate many elements of the configuration.
1593
 * Prefer `ostree_repo_write_config_and_reload`.
1594
 */
1595
gboolean
1596
ostree_repo_write_config (OstreeRepo *self, GKeyFile *new_config, GError **error)
1597
139
{
1598
139
  g_return_val_if_fail (self->inited, FALSE);
1599
1600
  /* Ensure that any remotes in the new config aren't defined in a
1601
   * separate config file.
1602
   */
1603
139
  gsize num_groups;
1604
139
  g_auto (GStrv) groups = g_key_file_get_groups (new_config, &num_groups);
1605
414
  for (gsize i = 0; i < num_groups; i++)
1606
275
    {
1607
275
      g_autoptr (OstreeRemote) new_remote = ostree_remote_new_from_keyfile (new_config, groups[i]);
1608
275
      if (new_remote != NULL)
1609
7
        {
1610
7
          g_autoptr (GError) local_error = NULL;
1611
1612
7
          g_autoptr (OstreeRemote) cur_remote
1613
7
              = _ostree_repo_get_remote (self, new_remote->name, &local_error);
1614
7
          if (cur_remote == NULL)
1615
7
            {
1616
7
              if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1617
0
                {
1618
0
                  g_propagate_error (error, g_steal_pointer (&local_error));
1619
0
                  return FALSE;
1620
0
                }
1621
7
            }
1622
0
          else if (cur_remote->file != NULL)
1623
0
            {
1624
0
              g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
1625
0
                           "Remote \"%s\" already defined in %s", new_remote->name,
1626
0
                           gs_file_get_path_cached (cur_remote->file));
1627
0
              return FALSE;
1628
0
            }
1629
7
        }
1630
275
    }
1631
1632
139
  gsize len;
1633
139
  g_autofree char *data = g_key_file_to_data (new_config, &len, error);
1634
139
  if (!glnx_file_replace_contents_at (self->repo_dir_fd, "config", (guint8 *)data, len, 0, NULL,
1635
139
                                      error))
1636
0
    return FALSE;
1637
1638
139
  g_key_file_free (self->config);
1639
139
  self->config = g_key_file_new ();
1640
139
  if (!g_key_file_load_from_data (self->config, data, len, 0, error))
1641
0
    return FALSE;
1642
1643
139
  return TRUE;
1644
139
}
1645
1646
/**
1647
 * ostree_repo_write_config_and_reload:
1648
 * @self: Repo
1649
 * @new_config: Overwrite the config file with this data, and reload
1650
 * @error: a #GError
1651
 *
1652
 * Save @new_config in place of this repository's config file and reload.
1653
 * The config will be validated.
1654
 */
1655
gboolean
1656
ostree_repo_write_config_and_reload (OstreeRepo *self, GKeyFile *new_config, GError **error)
1657
0
{
1658
0
  g_return_val_if_fail (self->inited, FALSE);
1659
1660
0
  g_autoptr (GKeyFile) old_config = g_steal_pointer (&self->config);
1661
  // Test reloading with the new config
1662
0
  self->config = new_config;
1663
0
  gboolean r = reload_config_inner (self, NULL, error);
1664
0
  self->config = g_steal_pointer (&old_config);
1665
0
  if (!r)
1666
0
    {
1667
      // Best effort to revert back to the old config, but if that fails
1668
      // we're in a doubly bad state.
1669
0
      (void)reload_config_inner (self, NULL, NULL);
1670
0
      return FALSE;
1671
0
    }
1672
  // Now perform the actual write
1673
0
  return ostree_repo_write_config (self, new_config, error);
1674
0
}
1675
1676
/* Bind a subset of an a{sv} to options in a given GKeyfile section */
1677
static void
1678
keyfile_set_from_vardict (GKeyFile *keyfile, const char *section, GVariant *vardict)
1679
0
{
1680
0
  GVariantIter viter;
1681
0
  const char *key;
1682
0
  GVariant *val;
1683
1684
0
  g_variant_iter_init (&viter, vardict);
1685
0
  while (g_variant_iter_loop (&viter, "{&s@v}", &key, &val))
1686
0
    {
1687
0
      g_autoptr (GVariant) child = g_variant_get_variant (val);
1688
0
      if (g_variant_is_of_type (child, G_VARIANT_TYPE_STRING))
1689
0
        g_key_file_set_string (keyfile, section, key, g_variant_get_string (child, NULL));
1690
0
      else if (g_variant_is_of_type (child, G_VARIANT_TYPE_BOOLEAN))
1691
0
        g_key_file_set_boolean (keyfile, section, key, g_variant_get_boolean (child));
1692
0
      else if (g_variant_is_of_type (child, G_VARIANT_TYPE_STRING_ARRAY))
1693
0
        {
1694
0
          gsize len;
1695
0
          g_autofree const gchar **strv_child = g_variant_get_strv (child, &len);
1696
0
          g_key_file_set_string_list (keyfile, section, key, strv_child, len);
1697
0
        }
1698
0
      else
1699
0
        g_critical ("Unhandled type '%s' in %s", (char *)g_variant_get_type (child), G_STRFUNC);
1700
0
    }
1701
0
}
1702
1703
static gboolean
1704
impl_repo_remote_add (OstreeRepo *self, GFile *sysroot, gboolean if_not_exists, const char *name,
1705
                      const char *url, GVariant *options, GCancellable *cancellable, GError **error)
1706
0
{
1707
0
  g_return_val_if_fail (name != NULL, FALSE);
1708
0
  g_return_val_if_fail (options == NULL || g_variant_is_of_type (options, G_VARIANT_TYPE ("a{sv}")),
1709
0
                        FALSE);
1710
1711
0
  if (!ostree_validate_remote_name (name, error))
1712
0
    return FALSE;
1713
1714
0
  g_autoptr (OstreeRemote) remote = _ostree_repo_get_remote (self, name, NULL);
1715
0
  if (remote != NULL && if_not_exists)
1716
0
    {
1717
      /* Note early return */
1718
0
      return TRUE;
1719
0
    }
1720
0
  else if (remote != NULL)
1721
0
    {
1722
0
      return glnx_throw (error, "Remote configuration for \"%s\" already exists: %s", name,
1723
0
                         remote->file ? gs_file_get_path_cached (remote->file) : "(in config)");
1724
0
    }
1725
1726
0
  remote = ostree_remote_new (name);
1727
1728
  /* Only add repos in remotes.d if the repo option
1729
   * add-remotes-config-dir is true. This is the default for system
1730
   * repos.
1731
   */
1732
0
  g_autoptr (GFile) etc_ostree_remotes_d = get_remotes_d_dir (self, sysroot);
1733
0
  if (etc_ostree_remotes_d && self->add_remotes_config_dir)
1734
0
    {
1735
0
      g_autoptr (GError) local_error = NULL;
1736
1737
0
      if (!g_file_make_directory_with_parents (etc_ostree_remotes_d, cancellable, &local_error))
1738
0
        {
1739
0
          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
1740
0
            {
1741
0
              g_clear_error (&local_error);
1742
0
            }
1743
0
          else
1744
0
            {
1745
0
              g_propagate_error (error, g_steal_pointer (&local_error));
1746
0
              return FALSE;
1747
0
            }
1748
0
        }
1749
1750
0
      g_autofree char *basename = g_strconcat (name, ".conf", NULL);
1751
0
      remote->file = g_file_get_child (etc_ostree_remotes_d, basename);
1752
0
    }
1753
1754
0
  if (url)
1755
0
    {
1756
0
      if (g_str_has_prefix (url, "metalink="))
1757
0
        g_key_file_set_string (remote->options, remote->group, "metalink",
1758
0
                               url + strlen ("metalink="));
1759
0
      else
1760
0
        g_key_file_set_string (remote->options, remote->group, "url", url);
1761
0
    }
1762
1763
0
  if (options)
1764
0
    keyfile_set_from_vardict (remote->options, remote->group, options);
1765
1766
0
  if (remote->file != NULL)
1767
0
    {
1768
0
      gsize length;
1769
0
      g_autofree char *data = g_key_file_to_data (remote->options, &length, NULL);
1770
1771
0
      if (!g_file_replace_contents (remote->file, data, length, NULL, FALSE, 0, NULL, cancellable,
1772
0
                                    error))
1773
0
        return FALSE;
1774
0
    }
1775
0
  else
1776
0
    {
1777
0
      g_autoptr (GKeyFile) config = NULL;
1778
1779
0
      config = ostree_repo_copy_config (self);
1780
0
      ot_keyfile_copy_group (remote->options, config, remote->group);
1781
1782
0
      if (!ostree_repo_write_config (self, config, error))
1783
0
        return FALSE;
1784
0
    }
1785
1786
0
  _ostree_repo_add_remote (self, remote);
1787
1788
0
  return TRUE;
1789
0
}
1790
1791
/**
1792
 * ostree_repo_remote_add:
1793
 * @self: Repo
1794
 * @name: Name of remote
1795
 * @url: (allow-none): URL for remote (if URL begins with metalink=, it will be used as such)
1796
 * @options: (allow-none): GVariant of type a{sv}
1797
 * @cancellable: Cancellable
1798
 * @error: Error
1799
 *
1800
 * Create a new remote named @name pointing to @url.  If @options is
1801
 * provided, then it will be mapped to #GKeyFile entries, where the
1802
 * GVariant dictionary key is an option string, and the value is
1803
 * mapped as follows:
1804
 *   * s: g_key_file_set_string()
1805
 *   * b: g_key_file_set_boolean()
1806
 *   * as: g_key_file_set_string_list()
1807
 *
1808
 */
1809
gboolean
1810
ostree_repo_remote_add (OstreeRepo *self, const char *name, const char *url, GVariant *options,
1811
                        GCancellable *cancellable, GError **error)
1812
0
{
1813
0
  return impl_repo_remote_add (self, NULL, FALSE, name, url, options, cancellable, error);
1814
0
}
1815
1816
static gboolean
1817
impl_repo_remote_delete (OstreeRepo *self, GFile *sysroot, gboolean if_exists, const char *name,
1818
                         GCancellable *cancellable, GError **error)
1819
0
{
1820
0
  g_return_val_if_fail (name != NULL, FALSE);
1821
1822
0
  if (!ostree_validate_remote_name (name, error))
1823
0
    return FALSE;
1824
1825
0
  g_autoptr (OstreeRemote) remote = NULL;
1826
0
  if (if_exists)
1827
0
    {
1828
0
      remote = _ostree_repo_get_remote (self, name, NULL);
1829
0
      if (!remote)
1830
0
        {
1831
          /* Note early return */
1832
0
          return TRUE;
1833
0
        }
1834
0
    }
1835
0
  else
1836
0
    remote = _ostree_repo_get_remote (self, name, error);
1837
1838
0
  if (remote == NULL)
1839
0
    return FALSE;
1840
1841
0
  if (remote->file != NULL)
1842
0
    {
1843
0
      if (!glnx_unlinkat (AT_FDCWD, gs_file_get_path_cached (remote->file), 0, error))
1844
0
        return FALSE;
1845
0
    }
1846
0
  else
1847
0
    {
1848
0
      g_autoptr (GKeyFile) config = ostree_repo_copy_config (self);
1849
1850
      /* XXX Not sure it's worth failing if the group to remove
1851
       *     isn't found.  It's the end result we want, after all. */
1852
0
      if (g_key_file_remove_group (config, remote->group, NULL))
1853
0
        {
1854
0
          if (!ostree_repo_write_config (self, config, error))
1855
0
            return FALSE;
1856
0
        }
1857
0
    }
1858
1859
  /* Delete the remote's keyring file, if it exists. */
1860
0
  if (!ot_ensure_unlinked_at (self->repo_dir_fd, remote->keyring, error))
1861
0
    return FALSE;
1862
1863
0
  _ostree_repo_remove_remote (self, remote);
1864
1865
0
  return TRUE;
1866
0
}
1867
1868
/**
1869
 * ostree_repo_remote_delete:
1870
 * @self: Repo
1871
 * @name: Name of remote
1872
 * @cancellable: Cancellable
1873
 * @error: Error
1874
 *
1875
 * Delete the remote named @name.  It is an error if the provided
1876
 * remote does not exist.
1877
 *
1878
 */
1879
gboolean
1880
ostree_repo_remote_delete (OstreeRepo *self, const char *name, GCancellable *cancellable,
1881
                           GError **error)
1882
0
{
1883
0
  return impl_repo_remote_delete (self, NULL, FALSE, name, cancellable, error);
1884
0
}
1885
1886
static gboolean
1887
impl_repo_remote_replace (OstreeRepo *self, GFile *sysroot, const char *name, const char *url,
1888
                          GVariant *options, GCancellable *cancellable, GError **error)
1889
0
{
1890
0
  g_return_val_if_fail (name != NULL, FALSE);
1891
0
  g_return_val_if_fail (options == NULL || g_variant_is_of_type (options, G_VARIANT_TYPE ("a{sv}")),
1892
0
                        FALSE);
1893
1894
0
  if (!ostree_validate_remote_name (name, error))
1895
0
    return FALSE;
1896
1897
0
  g_autoptr (GError) local_error = NULL;
1898
0
  g_autoptr (OstreeRemote) remote = _ostree_repo_get_remote (self, name, &local_error);
1899
0
  if (remote == NULL)
1900
0
    {
1901
0
      if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1902
0
        {
1903
0
          g_propagate_error (error, g_steal_pointer (&local_error));
1904
0
          return FALSE;
1905
0
        }
1906
0
      g_clear_error (&local_error);
1907
0
      if (!impl_repo_remote_add (self, sysroot, FALSE, name, url, options, cancellable, error))
1908
0
        return FALSE;
1909
0
    }
1910
0
  else
1911
0
    {
1912
      /* Replace the entire option group */
1913
0
      if (!g_key_file_remove_group (remote->options, remote->group, error))
1914
0
        return FALSE;
1915
1916
0
      if (url)
1917
0
        {
1918
0
          if (g_str_has_prefix (url, "metalink="))
1919
0
            g_key_file_set_string (remote->options, remote->group, "metalink",
1920
0
                                   url + strlen ("metalink="));
1921
0
          else
1922
0
            g_key_file_set_string (remote->options, remote->group, "url", url);
1923
0
        }
1924
1925
0
      if (options != NULL)
1926
0
        keyfile_set_from_vardict (remote->options, remote->group, options);
1927
1928
      /* Write out updated settings */
1929
0
      if (remote->file != NULL)
1930
0
        {
1931
0
          gsize length;
1932
0
          g_autofree char *data = g_key_file_to_data (remote->options, &length, NULL);
1933
1934
0
          if (!g_file_replace_contents (remote->file, data, length, NULL, FALSE, 0, NULL,
1935
0
                                        cancellable, error))
1936
0
            return FALSE;
1937
0
        }
1938
0
      else
1939
0
        {
1940
0
          g_autoptr (GKeyFile) config = ostree_repo_copy_config (self);
1941
1942
          /* Remove the existing group if it exists */
1943
0
          if (!g_key_file_remove_group (config, remote->group, &local_error))
1944
0
            {
1945
0
              if (!g_error_matches (local_error, G_KEY_FILE_ERROR,
1946
0
                                    G_KEY_FILE_ERROR_GROUP_NOT_FOUND))
1947
0
                {
1948
0
                  g_propagate_error (error, g_steal_pointer (&local_error));
1949
0
                  return FALSE;
1950
0
                }
1951
0
            }
1952
1953
0
          ot_keyfile_copy_group (remote->options, config, remote->group);
1954
0
          if (!ostree_repo_write_config (self, config, error))
1955
0
            return FALSE;
1956
0
        }
1957
0
    }
1958
1959
0
  return TRUE;
1960
0
}
1961
1962
/**
1963
 * ostree_repo_remote_change:
1964
 * @self: Repo
1965
 * @sysroot: (allow-none): System root
1966
 * @changeop: Operation to perform
1967
 * @name: Name of remote
1968
 * @url: (allow-none): URL for remote (if URL begins with metalink=, it will be used as such)
1969
 * @options: (allow-none): GVariant of type a{sv}
1970
 * @cancellable: Cancellable
1971
 * @error: Error
1972
 *
1973
 * A combined function handling the equivalent of
1974
 * ostree_repo_remote_add(), ostree_repo_remote_delete(), with more
1975
 * options.
1976
 *
1977
 *
1978
 */
1979
gboolean
1980
ostree_repo_remote_change (OstreeRepo *self, GFile *sysroot, OstreeRepoRemoteChange changeop,
1981
                           const char *name, const char *url, GVariant *options,
1982
                           GCancellable *cancellable, GError **error)
1983
0
{
1984
0
  switch (changeop)
1985
0
    {
1986
0
    case OSTREE_REPO_REMOTE_CHANGE_ADD:
1987
0
      return impl_repo_remote_add (self, sysroot, FALSE, name, url, options, cancellable, error);
1988
0
    case OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS:
1989
0
      return impl_repo_remote_add (self, sysroot, TRUE, name, url, options, cancellable, error);
1990
0
    case OSTREE_REPO_REMOTE_CHANGE_DELETE:
1991
0
      return impl_repo_remote_delete (self, sysroot, FALSE, name, cancellable, error);
1992
0
    case OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS:
1993
0
      return impl_repo_remote_delete (self, sysroot, TRUE, name, cancellable, error);
1994
0
    case OSTREE_REPO_REMOTE_CHANGE_REPLACE:
1995
0
      return impl_repo_remote_replace (self, sysroot, name, url, options, cancellable, error);
1996
0
    }
1997
0
  g_assert_not_reached ();
1998
0
}
1999
2000
static void
2001
_ostree_repo_remote_list (OstreeRepo *self, GHashTable *out)
2002
0
{
2003
0
  GHashTableIter iter;
2004
0
  gpointer key, value;
2005
2006
0
  g_mutex_lock (&self->remotes_lock);
2007
2008
0
  g_hash_table_iter_init (&iter, self->remotes);
2009
0
  while (g_hash_table_iter_next (&iter, &key, &value))
2010
0
    g_hash_table_insert (out, g_strdup (key), NULL);
2011
2012
0
  g_mutex_unlock (&self->remotes_lock);
2013
2014
0
  if (self->parent_repo)
2015
0
    _ostree_repo_remote_list (self->parent_repo, out);
2016
0
}
2017
2018
/**
2019
 * ostree_repo_remote_list:
2020
 * @self: Repo
2021
 * @out_n_remotes: (out) (allow-none): Number of remotes available
2022
 *
2023
 * List available remote names in an #OstreeRepo.  Remote names are sorted
2024
 * alphabetically.  If no remotes are available the function returns %NULL.
2025
 *
2026
 * Returns: (array length=out_n_remotes) (transfer full): a %NULL-terminated
2027
 *          array of remote names
2028
 **/
2029
char **
2030
ostree_repo_remote_list (OstreeRepo *self, guint *out_n_remotes)
2031
0
{
2032
0
  char **remotes = NULL;
2033
0
  guint n_remotes;
2034
0
  g_autoptr (GHashTable) remotes_ht = NULL;
2035
2036
0
  remotes_ht = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free,
2037
0
                                      (GDestroyNotify)NULL);
2038
2039
0
  _ostree_repo_remote_list (self, remotes_ht);
2040
2041
0
  n_remotes = g_hash_table_size (remotes_ht);
2042
2043
0
  if (n_remotes > 0)
2044
0
    {
2045
0
      GList *list, *link;
2046
0
      guint ii = 0;
2047
2048
0
      remotes = g_new (char *, n_remotes + 1);
2049
2050
0
      list = g_hash_table_get_keys (remotes_ht);
2051
0
      list = g_list_sort (list, (GCompareFunc)strcmp);
2052
2053
0
      for (link = list; link != NULL; link = link->next)
2054
0
        remotes[ii++] = g_strdup (link->data);
2055
2056
0
      g_list_free (list);
2057
2058
0
      remotes[ii] = NULL;
2059
0
    }
2060
2061
0
  if (out_n_remotes)
2062
0
    *out_n_remotes = n_remotes;
2063
2064
0
  return remotes;
2065
0
}
2066
2067
/**
2068
 * ostree_repo_remote_get_url:
2069
 * @self: Repo
2070
 * @name: Name of remote
2071
 * @out_url: (out) (optional): Remote's URL
2072
 * @error: Error
2073
 *
2074
 * Return the URL of the remote named @name through @out_url.  It is an
2075
 * error if the provided remote does not exist.
2076
 *
2077
 * Returns: %TRUE on success, %FALSE on failure
2078
 */
2079
gboolean
2080
ostree_repo_remote_get_url (OstreeRepo *self, const char *name, char **out_url, GError **error)
2081
0
{
2082
0
  g_return_val_if_fail (name != NULL, FALSE);
2083
2084
0
  g_autofree char *url = NULL;
2085
0
  if (_ostree_repo_remote_name_is_file (name))
2086
0
    {
2087
0
      url = g_strdup (name);
2088
0
    }
2089
0
  else
2090
0
    {
2091
0
      if (!ostree_repo_get_remote_option (self, name, "url", NULL, &url, error))
2092
0
        return FALSE;
2093
2094
0
      if (url == NULL)
2095
0
        {
2096
0
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
2097
0
                       "No \"url\" option in remote \"%s\"", name);
2098
0
          return FALSE;
2099
0
        }
2100
0
    }
2101
2102
0
  if (out_url != NULL)
2103
0
    *out_url = g_steal_pointer (&url);
2104
0
  return TRUE;
2105
0
}
2106
2107
/**
2108
 * ostree_repo_remote_get_gpg_verify:
2109
 * @self: Repo
2110
 * @name: Name of remote
2111
 * @out_gpg_verify: (out) (optional): Remote's GPG option
2112
 * @error: Error
2113
 *
2114
 * Return whether GPG verification is enabled for the remote named @name
2115
 * through @out_gpg_verify.  It is an error if the provided remote does
2116
 * not exist.
2117
 *
2118
 * Returns: %TRUE on success, %FALSE on failure
2119
 */
2120
gboolean
2121
ostree_repo_remote_get_gpg_verify (OstreeRepo *self, const char *name, gboolean *out_gpg_verify,
2122
                                   GError **error)
2123
0
{
2124
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
2125
0
  g_return_val_if_fail (name != NULL, FALSE);
2126
2127
  /* For compatibility with pull-local, don't GPG verify file:// URIs. */
2128
0
  if (_ostree_repo_remote_name_is_file (name))
2129
0
    {
2130
0
      if (out_gpg_verify != NULL)
2131
0
        *out_gpg_verify = FALSE;
2132
0
      return TRUE;
2133
0
    }
2134
2135
0
  return ostree_repo_get_remote_boolean_option (self, name, "gpg-verify", TRUE, out_gpg_verify,
2136
0
                                                error);
2137
0
}
2138
2139
/**
2140
 * ostree_repo_remote_get_gpg_verify_summary:
2141
 * @self: Repo
2142
 * @name: Name of remote
2143
 * @out_gpg_verify_summary: (out) (allow-none): Remote's GPG option
2144
 * @error: Error
2145
 *
2146
 * Return whether GPG verification of the summary is enabled for the remote
2147
 * named @name through @out_gpg_verify_summary.  It is an error if the provided
2148
 * remote does not exist.
2149
 *
2150
 * Returns: %TRUE on success, %FALSE on failure
2151
 */
2152
gboolean
2153
ostree_repo_remote_get_gpg_verify_summary (OstreeRepo *self, const char *name,
2154
                                           gboolean *out_gpg_verify_summary, GError **error)
2155
0
{
2156
0
  return ostree_repo_get_remote_boolean_option (self, name, "gpg-verify-summary", FALSE,
2157
0
                                                out_gpg_verify_summary, error);
2158
0
}
2159
2160
/**
2161
 * ostree_repo_remote_gpg_import:
2162
 * @self: Self
2163
 * @name: name of a remote
2164
 * @source_stream: (nullable): a #GInputStream, or %NULL
2165
 * @key_ids: (array zero-terminated=1) (element-type utf8) (nullable): a %NULL-terminated array of
2166
 * GPG key IDs, or %NULL
2167
 * @out_imported: (out) (optional): return location for the number of imported
2168
 *                              keys, or %NULL
2169
 * @cancellable: a #GCancellable
2170
 * @error: a #GError
2171
 *
2172
 * Imports one or more GPG keys from the open @source_stream, or from the
2173
 * user's personal keyring if @source_stream is %NULL.  The @key_ids array
2174
 * can optionally restrict which keys are imported.  If @key_ids is %NULL,
2175
 * then all keys are imported.
2176
 *
2177
 * The imported keys will be used to conduct GPG verification when pulling
2178
 * from the remote named @name.
2179
 *
2180
 * Returns: %TRUE on success, %FALSE on failure
2181
 */
2182
gboolean
2183
ostree_repo_remote_gpg_import (OstreeRepo *self, const char *name, GInputStream *source_stream,
2184
                               const char *const *key_ids, guint *out_imported,
2185
                               GCancellable *cancellable, GError **error)
2186
0
{
2187
0
#ifndef OSTREE_DISABLE_GPGME
2188
0
  OstreeRemote *remote;
2189
0
  g_auto (gpgme_ctx_t) source_context = NULL;
2190
0
  g_auto (gpgme_ctx_t) target_context = NULL;
2191
0
  g_auto (gpgme_data_t) data_buffer = NULL;
2192
0
  gpgme_import_result_t import_result;
2193
0
  gpgme_import_status_t import_status;
2194
0
  g_autofree char *source_tmp_dir = NULL;
2195
0
  g_autofree char *target_tmp_dir = NULL;
2196
0
  glnx_autofd int target_temp_fd = -1;
2197
0
  g_autoptr (GPtrArray) keys = NULL;
2198
0
  struct stat stbuf;
2199
0
  gpgme_error_t gpg_error;
2200
0
  gboolean ret = FALSE;
2201
2202
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
2203
0
  g_return_val_if_fail (name != NULL, FALSE);
2204
2205
  /* First make sure the remote name is valid. */
2206
2207
0
  remote = _ostree_repo_get_remote_inherited (self, name, error);
2208
0
  if (remote == NULL)
2209
0
    goto out;
2210
2211
  /* Prepare the source GPGME context.  If reading GPG keys from an input
2212
   * stream, point the OpenPGP engine at a temporary directory and import
2213
   * the keys to a new pubring.gpg file.  If the key data format is ASCII
2214
   * armored, this step will convert them to binary. */
2215
2216
0
  source_context = ot_gpgme_new_ctx (NULL, error);
2217
0
  if (!source_context)
2218
0
    goto out;
2219
2220
0
  if (source_stream != NULL)
2221
0
    {
2222
0
      data_buffer = ot_gpgme_data_input (source_stream);
2223
2224
0
      if (!ot_gpgme_ctx_tmp_home_dir (source_context, &source_tmp_dir, NULL, cancellable, error))
2225
0
        {
2226
0
          g_prefix_error (error, "Unable to configure context: ");
2227
0
          goto out;
2228
0
        }
2229
2230
0
      gpg_error = gpgme_op_import (source_context, data_buffer);
2231
0
      if (gpg_error != GPG_ERR_NO_ERROR)
2232
0
        {
2233
0
          ot_gpgme_throw (gpg_error, error, "Unable to import keys");
2234
0
          goto out;
2235
0
        }
2236
2237
0
      g_clear_pointer (&data_buffer, gpgme_data_release);
2238
0
    }
2239
2240
  /* Retrieve all keys or specific keys from the source GPGME context.
2241
   * Assemble a NULL-terminated array of gpgme_key_t structs to import. */
2242
2243
  /* The keys array will contain a NULL terminator, but it turns out,
2244
   * although not documented, gpgme_key_unref() gracefully handles it. */
2245
0
  keys = g_ptr_array_new_with_free_func ((GDestroyNotify)gpgme_key_unref);
2246
2247
0
  if (key_ids != NULL)
2248
0
    {
2249
0
      guint ii;
2250
2251
0
      for (ii = 0; key_ids[ii] != NULL; ii++)
2252
0
        {
2253
0
          gpgme_key_t key = NULL;
2254
2255
0
          gpg_error = gpgme_get_key (source_context, key_ids[ii], &key, 0);
2256
0
          if (gpg_error != GPG_ERR_NO_ERROR)
2257
0
            {
2258
0
              ot_gpgme_throw (gpg_error, error, "Unable to find key \"%s\"", key_ids[ii]);
2259
0
              goto out;
2260
0
            }
2261
2262
          /* Transfer ownership. */
2263
0
          g_ptr_array_add (keys, key);
2264
0
        }
2265
0
    }
2266
0
  else
2267
0
    {
2268
0
      gpg_error = gpgme_op_keylist_start (source_context, NULL, 0);
2269
2270
0
      while (gpg_error == GPG_ERR_NO_ERROR)
2271
0
        {
2272
0
          gpgme_key_t key = NULL;
2273
2274
0
          gpg_error = gpgme_op_keylist_next (source_context, &key);
2275
2276
0
          if (gpg_error != GPG_ERR_NO_ERROR)
2277
0
            break;
2278
2279
          /* Transfer ownership. */
2280
0
          g_ptr_array_add (keys, key);
2281
0
        }
2282
2283
0
      if (gpgme_err_code (gpg_error) != GPG_ERR_EOF)
2284
0
        {
2285
0
          ot_gpgme_throw (gpg_error, error, "Unable to list keys");
2286
0
          goto out;
2287
0
        }
2288
0
    }
2289
2290
  /* Add the NULL terminator. */
2291
0
  g_ptr_array_add (keys, NULL);
2292
2293
  /* Prepare the target GPGME context to serve as the import destination.
2294
   * Here the pubring.gpg file in a second temporary directory is a copy
2295
   * of the remote's keyring file.  We'll let the import operation alter
2296
   * the pubring.gpg file, then rename it back to its permanent home. */
2297
2298
0
  target_context = ot_gpgme_new_ctx (NULL, error);
2299
0
  if (!target_context)
2300
0
    goto out;
2301
2302
  /* No need for an output stream since we copy in a pubring.gpg. */
2303
0
  if (!ot_gpgme_ctx_tmp_home_dir (target_context, &target_tmp_dir, NULL, cancellable, error))
2304
0
    {
2305
0
      g_prefix_error (error, "Unable to configure context: ");
2306
0
      goto out;
2307
0
    }
2308
2309
0
  if (!glnx_opendirat (AT_FDCWD, target_tmp_dir, FALSE, &target_temp_fd, error))
2310
0
    {
2311
0
      g_prefix_error (error, "Unable to open directory: ");
2312
0
      goto out;
2313
0
    }
2314
2315
0
  if (fstatat (self->repo_dir_fd, remote->keyring, &stbuf, AT_SYMLINK_NOFOLLOW) == 0)
2316
0
    {
2317
0
      if (!glnx_file_copy_at (self->repo_dir_fd, remote->keyring, &stbuf, target_temp_fd,
2318
0
                              "pubring.gpg", GLNX_FILE_COPY_NOXATTRS, cancellable, error))
2319
0
        {
2320
0
          g_prefix_error (error, "Unable to copy remote's keyring: ");
2321
0
          goto out;
2322
0
        }
2323
0
    }
2324
0
  else if (errno == ENOENT)
2325
0
    {
2326
0
      glnx_autofd int fd = -1;
2327
2328
      /* Create an empty pubring.gpg file prior to importing keys.  This
2329
       * prevents gpg2 from creating a pubring.kbx file in the new keybox
2330
       * format [1].  We want to stay with the older keyring format since
2331
       * its performance issues are not relevant here.
2332
       *
2333
       * [1] https://gnupg.org/faq/whats-new-in-2.1.html#keybox
2334
       */
2335
0
      fd = openat (target_temp_fd, "pubring.gpg", O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY, 0644);
2336
0
      if (fd == -1)
2337
0
        {
2338
0
          glnx_set_prefix_error_from_errno (error, "%s", "Unable to create pubring.gpg");
2339
0
          goto out;
2340
0
        }
2341
0
    }
2342
0
  else
2343
0
    {
2344
0
      glnx_set_prefix_error_from_errno (error, "%s", "Unable to copy remote's keyring");
2345
0
      goto out;
2346
0
    }
2347
2348
  /* Export the selected keys from the source context and import them into
2349
   * the target context. */
2350
2351
0
  gpg_error = gpgme_data_new (&data_buffer);
2352
0
  if (gpg_error != GPG_ERR_NO_ERROR)
2353
0
    {
2354
0
      ot_gpgme_throw (gpg_error, error, "Unable to create data buffer");
2355
0
      goto out;
2356
0
    }
2357
2358
0
  gpg_error = gpgme_op_export_keys (source_context, (gpgme_key_t *)keys->pdata, 0, data_buffer);
2359
0
  if (gpg_error != GPG_ERR_NO_ERROR)
2360
0
    {
2361
0
      ot_gpgme_throw (gpg_error, error, "Unable to export keys");
2362
0
      goto out;
2363
0
    }
2364
2365
0
  (void)gpgme_data_seek (data_buffer, 0, SEEK_SET);
2366
2367
0
  gpg_error = gpgme_op_import (target_context, data_buffer);
2368
0
  if (gpg_error != GPG_ERR_NO_ERROR)
2369
0
    {
2370
0
      ot_gpgme_throw (gpg_error, error, "Unable to import keys");
2371
0
      goto out;
2372
0
    }
2373
2374
0
  import_result = gpgme_op_import_result (target_context);
2375
0
  g_return_val_if_fail (import_result != NULL, FALSE);
2376
2377
  /* Check the status of each import and fail on the first error.
2378
   * All imports must be successful to update the remote's keyring. */
2379
0
  for (import_status = import_result->imports; import_status != NULL;
2380
0
       import_status = import_status->next)
2381
0
    {
2382
0
      if (import_status->result != GPG_ERR_NO_ERROR)
2383
0
        {
2384
0
          ot_gpgme_throw (gpg_error, error, "Unable to import key \"%s\"", import_status->fpr);
2385
0
          goto out;
2386
0
        }
2387
0
    }
2388
2389
  /* Import successful; replace the remote's old keyring with the
2390
   * updated keyring in the target context's temporary directory. */
2391
0
  if (!glnx_file_copy_at (target_temp_fd, "pubring.gpg", NULL, self->repo_dir_fd, remote->keyring,
2392
0
                          GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_OVERWRITE, cancellable, error))
2393
0
    goto out;
2394
2395
0
  if (out_imported != NULL)
2396
0
    *out_imported = (guint)import_result->imported;
2397
2398
0
  ret = TRUE;
2399
2400
0
out:
2401
0
  if (remote != NULL)
2402
0
    ostree_remote_unref (remote);
2403
2404
0
  if (source_tmp_dir != NULL)
2405
0
    {
2406
0
      ot_gpgme_kill_agent (source_tmp_dir);
2407
0
      (void)glnx_shutil_rm_rf_at (AT_FDCWD, source_tmp_dir, NULL, NULL);
2408
0
    }
2409
2410
0
  if (target_tmp_dir != NULL)
2411
0
    {
2412
0
      ot_gpgme_kill_agent (target_tmp_dir);
2413
0
      (void)glnx_shutil_rm_rf_at (AT_FDCWD, target_tmp_dir, NULL, NULL);
2414
0
    }
2415
2416
0
  g_prefix_error (error, "GPG: ");
2417
2418
0
  return ret;
2419
#else  /* OSTREE_DISABLE_GPGME */
2420
  return glnx_throw (error, "GPG feature is disabled in a build time");
2421
#endif /* OSTREE_DISABLE_GPGME */
2422
0
}
2423
2424
#ifndef OSTREE_DISABLE_GPGME
2425
static gboolean _ostree_repo_gpg_prepare_verifier (OstreeRepo *self, const gchar *remote_name,
2426
                                                   GFile *keyringdir, GFile *extra_keyring,
2427
                                                   gboolean add_global_keyrings,
2428
                                                   OstreeGpgVerifier **out_verifier,
2429
                                                   GCancellable *cancellable, GError **error);
2430
#endif /* OSTREE_DISABLE_GPGME */
2431
2432
/**
2433
 * ostree_repo_remote_get_gpg_keys:
2434
 * @self: an #OstreeRepo
2435
 * @name: (nullable): name of the remote or %NULL
2436
 * @key_ids: (array zero-terminated=1) (element-type utf8) (nullable):
2437
 *    a %NULL-terminated array of GPG key IDs to include, or %NULL
2438
 * @out_keys: (out) (optional) (element-type GVariant) (transfer container):
2439
 *    return location for a #GPtrArray of the remote's trusted GPG keys, or
2440
 *    %NULL
2441
 * @cancellable: (nullable): a #GCancellable, or %NULL
2442
 * @error: return location for a #GError, or %NULL
2443
 *
2444
 * Enumerate the trusted GPG keys for the remote @name. If @name is
2445
 * %NULL, the global GPG keys will be returned. The keys will be
2446
 * returned in the @out_keys #GPtrArray. Each element in the array is a
2447
 * #GVariant of format %OSTREE_GPG_KEY_GVARIANT_FORMAT. The @key_ids
2448
 * array can be used to limit which keys are included. If @key_ids is
2449
 * %NULL, then all keys are included.
2450
 *
2451
 * Returns: %TRUE if the GPG keys could be enumerated, %FALSE otherwise
2452
 *
2453
 * Since: 2021.4
2454
 */
2455
gboolean
2456
ostree_repo_remote_get_gpg_keys (OstreeRepo *self, const char *name, const char *const *key_ids,
2457
                                 GPtrArray **out_keys, GCancellable *cancellable, GError **error)
2458
0
{
2459
0
#ifndef OSTREE_DISABLE_GPGME
2460
0
  g_autoptr (OstreeGpgVerifier) verifier = NULL;
2461
0
  gboolean global_keyrings = (name == NULL);
2462
0
  if (!_ostree_repo_gpg_prepare_verifier (self, name, NULL, NULL, global_keyrings, &verifier,
2463
0
                                          cancellable, error))
2464
0
    return FALSE;
2465
2466
0
  g_autoptr (GPtrArray) gpg_keys = NULL;
2467
0
  if (!_ostree_gpg_verifier_list_keys (verifier, key_ids, &gpg_keys, cancellable, error))
2468
0
    return FALSE;
2469
2470
0
  g_autoptr (GPtrArray) keys = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
2471
0
  for (guint i = 0; i < gpg_keys->len; i++)
2472
0
    {
2473
0
      gpgme_key_t key = gpg_keys->pdata[i];
2474
2475
0
      g_auto (GVariantBuilder) subkeys_builder = OT_VARIANT_BUILDER_INITIALIZER;
2476
0
      g_variant_builder_init (&subkeys_builder, G_VARIANT_TYPE ("aa{sv}"));
2477
0
      g_auto (GVariantBuilder) uids_builder = OT_VARIANT_BUILDER_INITIALIZER;
2478
0
      g_variant_builder_init (&uids_builder, G_VARIANT_TYPE ("aa{sv}"));
2479
0
      for (gpgme_subkey_t subkey = key->subkeys; subkey != NULL; subkey = subkey->next)
2480
0
        {
2481
0
          g_auto (GVariantDict) subkey_dict = OT_VARIANT_BUILDER_INITIALIZER;
2482
0
          g_variant_dict_init (&subkey_dict, NULL);
2483
0
          g_variant_dict_insert_value (&subkey_dict, "fingerprint",
2484
0
                                       g_variant_new_string (subkey->fpr));
2485
0
          g_variant_dict_insert_value (&subkey_dict, "created",
2486
0
                                       g_variant_new_int64 (GINT64_TO_BE (subkey->timestamp)));
2487
0
          g_variant_dict_insert_value (&subkey_dict, "expires",
2488
0
                                       g_variant_new_int64 (GINT64_TO_BE (subkey->expires)));
2489
0
          g_variant_dict_insert_value (&subkey_dict, "revoked",
2490
0
                                       g_variant_new_boolean (subkey->revoked));
2491
0
          g_variant_dict_insert_value (&subkey_dict, "expired",
2492
0
                                       g_variant_new_boolean (subkey->expired));
2493
0
          g_variant_dict_insert_value (&subkey_dict, "invalid",
2494
0
                                       g_variant_new_boolean (subkey->invalid));
2495
0
          g_variant_builder_add (&subkeys_builder, "@a{sv}", g_variant_dict_end (&subkey_dict));
2496
0
        }
2497
2498
0
      for (gpgme_user_id_t uid = key->uids; uid != NULL; uid = uid->next)
2499
0
        {
2500
          /* Get WKD update URLs if address set */
2501
0
          g_autofree char *advanced_url = NULL;
2502
0
          g_autofree char *direct_url = NULL;
2503
0
          if (uid->address != NULL)
2504
0
            {
2505
0
              if (!ot_gpg_wkd_urls (uid->address, &advanced_url, &direct_url, error))
2506
0
                return FALSE;
2507
0
            }
2508
2509
0
          g_auto (GVariantDict) uid_dict = OT_VARIANT_BUILDER_INITIALIZER;
2510
0
          g_variant_dict_init (&uid_dict, NULL);
2511
0
          g_variant_dict_insert_value (&uid_dict, "uid", g_variant_new_string (uid->uid));
2512
0
          g_variant_dict_insert_value (&uid_dict, "name", g_variant_new_string (uid->name));
2513
0
          g_variant_dict_insert_value (&uid_dict, "comment", g_variant_new_string (uid->comment));
2514
0
          g_variant_dict_insert_value (&uid_dict, "email", g_variant_new_string (uid->email));
2515
0
          g_variant_dict_insert_value (&uid_dict, "revoked", g_variant_new_boolean (uid->revoked));
2516
0
          g_variant_dict_insert_value (&uid_dict, "invalid", g_variant_new_boolean (uid->invalid));
2517
0
          g_variant_dict_insert_value (&uid_dict, "advanced_url",
2518
0
                                       g_variant_new ("ms", advanced_url));
2519
0
          g_variant_dict_insert_value (&uid_dict, "direct_url", g_variant_new ("ms", direct_url));
2520
0
          g_variant_builder_add (&uids_builder, "@a{sv}", g_variant_dict_end (&uid_dict));
2521
0
        }
2522
2523
      /* Currently empty */
2524
0
      g_auto (GVariantDict) metadata_dict = OT_VARIANT_BUILDER_INITIALIZER;
2525
0
      g_variant_dict_init (&metadata_dict, NULL);
2526
2527
0
      GVariant *key_variant = g_variant_new (
2528
0
          "(@aa{sv}@aa{sv}@a{sv})", g_variant_builder_end (&subkeys_builder),
2529
0
          g_variant_builder_end (&uids_builder), g_variant_dict_end (&metadata_dict));
2530
0
      g_ptr_array_add (keys, g_variant_ref_sink (key_variant));
2531
0
    }
2532
2533
0
  if (out_keys)
2534
0
    *out_keys = g_steal_pointer (&keys);
2535
2536
0
  return TRUE;
2537
#else  /* OSTREE_DISABLE_GPGME */
2538
  return glnx_throw (error, "GPG feature is disabled in a build time");
2539
#endif /* OSTREE_DISABLE_GPGME */
2540
0
}
2541
2542
/**
2543
 * ostree_repo_remote_fetch_summary:
2544
 * @self: Self
2545
 * @name: name of a remote
2546
 * @out_summary: (out) (optional): return location for raw summary data, or
2547
 *               %NULL
2548
 * @out_signatures: (out) (optional): return location for raw summary
2549
 *                  signature data, or %NULL
2550
 * @cancellable: a #GCancellable
2551
 * @error: a #GError
2552
 *
2553
 * Tries to fetch the summary file and any GPG signatures on the summary file
2554
 * over HTTP, and returns the binary data in @out_summary and @out_signatures
2555
 * respectively.
2556
 *
2557
 * If no summary file exists on the remote server, @out_summary is set to
2558
 * @NULL.  Likewise if the summary file is not signed, @out_signatures is
2559
 * set to @NULL.  In either case the function still returns %TRUE.
2560
 *
2561
 * This method does not verify the signature of the downloaded summary file.
2562
 * Use ostree_repo_verify_summary() for that.
2563
 *
2564
 * Parse the summary data into a #GVariant using g_variant_new_from_bytes()
2565
 * with #OSTREE_SUMMARY_GVARIANT_FORMAT as the format string.
2566
 *
2567
 * Returns: %TRUE on success, %FALSE on failure
2568
 */
2569
gboolean
2570
ostree_repo_remote_fetch_summary (OstreeRepo *self, const char *name, GBytes **out_summary,
2571
                                  GBytes **out_signatures, GCancellable *cancellable,
2572
                                  GError **error)
2573
0
{
2574
0
  return ostree_repo_remote_fetch_summary_with_options (self, name, NULL, out_summary,
2575
0
                                                        out_signatures, cancellable, error);
2576
0
}
2577
2578
static gboolean
2579
ostree_repo_mode_to_string (OstreeRepoMode mode, const char **out_mode, GError **error)
2580
139
{
2581
139
  const char *ret_mode;
2582
2583
139
  switch (mode)
2584
139
    {
2585
0
    case OSTREE_REPO_MODE_BARE:
2586
0
      ret_mode = "bare";
2587
0
      break;
2588
0
    case OSTREE_REPO_MODE_BARE_USER:
2589
0
      ret_mode = "bare-user";
2590
0
      break;
2591
0
    case OSTREE_REPO_MODE_BARE_USER_ONLY:
2592
0
      ret_mode = "bare-user-only";
2593
0
      break;
2594
139
    case OSTREE_REPO_MODE_ARCHIVE:
2595
      /* Legacy alias */
2596
139
      ret_mode = "archive-z2";
2597
139
      break;
2598
0
    case OSTREE_REPO_MODE_BARE_SPLIT_XATTRS:
2599
0
      ret_mode = "bare-split-xattrs";
2600
0
      break;
2601
0
    default:
2602
0
      return glnx_throw (error, "Invalid mode '%d'", mode);
2603
139
    }
2604
2605
139
  *out_mode = ret_mode;
2606
139
  return TRUE;
2607
139
}
2608
2609
/**
2610
 * ostree_repo_mode_from_string:
2611
 * @mode: a repo mode as a string
2612
 * @out_mode: (out): the corresponding #OstreeRepoMode
2613
 * @error: a #GError if the string is not a valid mode
2614
 */
2615
gboolean
2616
ostree_repo_mode_from_string (const char *mode, OstreeRepoMode *out_mode, GError **error)
2617
228
{
2618
228
  OstreeRepoMode ret_mode;
2619
2620
228
  if (strcmp (mode, "bare") == 0)
2621
0
    ret_mode = OSTREE_REPO_MODE_BARE;
2622
228
  else if (strcmp (mode, "bare-user") == 0)
2623
0
    ret_mode = OSTREE_REPO_MODE_BARE_USER;
2624
228
  else if (strcmp (mode, "bare-user-only") == 0)
2625
0
    ret_mode = OSTREE_REPO_MODE_BARE_USER_ONLY;
2626
228
  else if (strcmp (mode, "archive-z2") == 0 || strcmp (mode, "archive") == 0)
2627
228
    ret_mode = OSTREE_REPO_MODE_ARCHIVE;
2628
0
  else if (strcmp (mode, "bare-split-xattrs") == 0)
2629
0
    ret_mode = OSTREE_REPO_MODE_BARE_SPLIT_XATTRS;
2630
0
  else
2631
0
    return glnx_throw (error, "Invalid mode '%s' in repository configuration", mode);
2632
2633
228
  *out_mode = ret_mode;
2634
228
  return TRUE;
2635
228
}
2636
2637
#define DEFAULT_CONFIG_CONTENTS \
2638
139
  ("[core]\n" \
2639
139
   "repo_version=1\n")
2640
2641
/* Just write the dirs to disk, return a dfd */
2642
static gboolean
2643
repo_create_at_internal (int dfd, const char *path, OstreeRepoMode mode, GVariant *options,
2644
                         int *out_dfd, GCancellable *cancellable, GError **error)
2645
139
{
2646
139
  GLNX_AUTO_PREFIX_ERROR ("Creating repo", error);
2647
139
  struct stat stbuf;
2648
  /* We do objects/ last - if it exists we do nothing and exit successfully */
2649
139
  const char *state_dirs[] = { "tmp",        "extensions",   "state",        "refs",
2650
139
                               "refs/heads", "refs/mirrors", "refs/remotes", "objects" };
2651
2652
  /* Early return if we have an existing repo */
2653
139
  {
2654
139
    g_autofree char *objects_path = g_build_filename (path, "objects", NULL);
2655
2656
139
    if (!glnx_fstatat_allow_noent (dfd, objects_path, &stbuf, 0, error))
2657
0
      return FALSE;
2658
139
    if (errno == 0)
2659
0
      {
2660
0
        glnx_autofd int repo_dfd = -1;
2661
0
        if (!glnx_opendirat (dfd, path, TRUE, &repo_dfd, error))
2662
0
          return FALSE;
2663
2664
        /* Note early return */
2665
0
        *out_dfd = g_steal_fd (&repo_dfd);
2666
0
        return TRUE;
2667
0
      }
2668
139
  }
2669
2670
139
  if (mkdirat (dfd, path, DEFAULT_DIRECTORY_MODE) != 0)
2671
139
    {
2672
139
      if (G_UNLIKELY (errno != EEXIST))
2673
0
        return glnx_throw_errno_prefix (error, "mkdirat");
2674
139
    }
2675
2676
139
  glnx_autofd int repo_dfd = -1;
2677
139
  if (!glnx_opendirat (dfd, path, TRUE, &repo_dfd, error))
2678
0
    return FALSE;
2679
2680
139
  if (!glnx_fstatat_allow_noent (repo_dfd, "config", &stbuf, 0, error))
2681
0
    return FALSE;
2682
139
  if (errno == ENOENT)
2683
139
    {
2684
139
      const char *mode_str = NULL;
2685
139
      g_autoptr (GString) config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
2686
2687
139
      if (!ostree_repo_mode_to_string (mode, &mode_str, error))
2688
0
        return FALSE;
2689
139
      g_assert (mode_str);
2690
2691
139
      g_string_append_printf (config_data, "mode=%s\n", mode_str);
2692
2693
139
      const char *collection_id = NULL;
2694
139
      if (options)
2695
0
        (void)g_variant_lookup (options, "collection-id", "&s", &collection_id);
2696
139
      if (collection_id != NULL)
2697
0
        g_string_append_printf (config_data, "collection-id=%s\n", collection_id);
2698
2699
139
      if (!glnx_file_replace_contents_at (repo_dfd, "config", (guint8 *)config_data->str,
2700
139
                                          config_data->len, 0, cancellable, error))
2701
0
        return FALSE;
2702
139
    }
2703
2704
1.25k
  for (guint i = 0; i < G_N_ELEMENTS (state_dirs); i++)
2705
1.11k
    {
2706
1.11k
      const char *elt = state_dirs[i];
2707
1.11k
      if (mkdirat (repo_dfd, elt, DEFAULT_DIRECTORY_MODE) == -1)
2708
0
        {
2709
0
          if (G_UNLIKELY (errno != EEXIST))
2710
0
            return glnx_throw_errno_prefix (error, "mkdirat");
2711
0
        }
2712
1.11k
    }
2713
2714
  /* Test that the fs supports user xattrs now, so we get an error early rather
2715
   * than during an object write later.
2716
   */
2717
139
  if (mode == OSTREE_REPO_MODE_BARE_USER)
2718
0
    {
2719
0
      g_auto (GLnxTmpfile) tmpf = {
2720
0
        0,
2721
0
      };
2722
2723
0
      if (!glnx_open_tmpfile_linkable_at (repo_dfd, ".", O_RDWR | O_CLOEXEC, &tmpf, error))
2724
0
        return FALSE;
2725
0
      if (!_ostree_write_bareuser_metadata (tmpf.fd, 0, 0, 644, NULL, error))
2726
0
        return FALSE;
2727
0
    }
2728
2729
139
  *out_dfd = g_steal_fd (&repo_dfd);
2730
139
  return TRUE;
2731
139
}
2732
2733
/**
2734
 * ostree_repo_create:
2735
 * @self: An #OstreeRepo
2736
 * @mode: The mode to store the repository in
2737
 * @cancellable: Cancellable
2738
 * @error: Error
2739
 *
2740
 * Create the underlying structure on disk for the repository, and call
2741
 * ostree_repo_open() on the result, preparing it for use.
2742
2743
 * Since version 2016.8, this function will succeed on an existing
2744
 * repository, and finish creating any necessary files in a partially
2745
 * created repository.  However, this function cannot change the mode
2746
 * of an existing repository, and will silently ignore an attempt to
2747
 * do so.
2748
 *
2749
 * Since 2017.9, "existing repository" is defined by the existence of an
2750
 * `objects` subdirectory.
2751
 *
2752
 * This function predates ostree_repo_create_at(). It is an error to call
2753
 * this function on a repository initialized via ostree_repo_open_at().
2754
 */
2755
gboolean
2756
ostree_repo_create (OstreeRepo *self, OstreeRepoMode mode, GCancellable *cancellable,
2757
                    GError **error)
2758
0
{
2759
0
  g_return_val_if_fail (self->repodir, FALSE);
2760
0
  const char *repopath = gs_file_get_path_cached (self->repodir);
2761
0
  g_autoptr (GVariantBuilder) builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
2762
0
  if (self->collection_id)
2763
0
    g_variant_builder_add (builder, "{s@v}", "collection-id",
2764
0
                           g_variant_new_variant (g_variant_new_string (self->collection_id)));
2765
2766
0
  glnx_autofd int repo_dir_fd = -1;
2767
0
  g_autoptr (GVariant) options = g_variant_ref_sink (g_variant_builder_end (builder));
2768
0
  if (!repo_create_at_internal (AT_FDCWD, repopath, mode, options, &repo_dir_fd, cancellable,
2769
0
                                error))
2770
0
    return FALSE;
2771
0
  self->repo_dir_fd = g_steal_fd (&repo_dir_fd);
2772
0
  if (!ostree_repo_open (self, cancellable, error))
2773
0
    return FALSE;
2774
0
  return TRUE;
2775
0
}
2776
2777
/**
2778
 * ostree_repo_create_at:
2779
 * @dfd: Directory fd
2780
 * @path: Path
2781
 * @mode: The mode to store the repository in
2782
 * @options: (nullable): a{sv}: See below for accepted keys
2783
 * @cancellable: Cancellable
2784
 * @error: Error
2785
 *
2786
 * This is a file-descriptor relative version of ostree_repo_create().
2787
 * Create the underlying structure on disk for the repository, and call
2788
 * ostree_repo_open_at() on the result, preparing it for use.
2789
 *
2790
 * If a repository already exists at @dfd + @path (defined by an `objects/`
2791
 * subdirectory existing), then this function will simply call
2792
 * ostree_repo_open_at().  In other words, this function cannot be used to change
2793
 * the mode or configuration (`repo/config`) of an existing repo.
2794
 *
2795
 * The @options dict may contain:
2796
 *
2797
 *   - collection-id: s: Set as collection ID in repo/config (Since 2017.9)
2798
 *
2799
 * Returns: (transfer full): A new OSTree repository reference
2800
 *
2801
 * Since: 2017.10
2802
 */
2803
OstreeRepo *
2804
ostree_repo_create_at (int dfd, const char *path, OstreeRepoMode mode, GVariant *options,
2805
                       GCancellable *cancellable, GError **error)
2806
139
{
2807
139
  glnx_autofd int repo_dfd = -1;
2808
139
  if (!repo_create_at_internal (dfd, path, mode, options, &repo_dfd, cancellable, error))
2809
0
    return NULL;
2810
139
  return repo_open_at_take_fd (&repo_dfd, cancellable, error);
2811
139
}
2812
2813
static gboolean
2814
enumerate_directory_allow_noent (GFile *dirpath, const char *queryargs,
2815
                                 GFileQueryInfoFlags queryflags, GFileEnumerator **out_direnum,
2816
                                 GCancellable *cancellable, GError **error)
2817
0
{
2818
0
  g_autoptr (GError) temp_error = NULL;
2819
0
  g_autoptr (GFileEnumerator) ret_direnum = NULL;
2820
2821
0
  ret_direnum
2822
0
      = g_file_enumerate_children (dirpath, queryargs, queryflags, cancellable, &temp_error);
2823
0
  if (!ret_direnum)
2824
0
    {
2825
0
      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
2826
0
        g_clear_error (&temp_error);
2827
0
      else
2828
0
        {
2829
0
          g_propagate_error (error, g_steal_pointer (&temp_error));
2830
0
          return FALSE;
2831
0
        }
2832
0
    }
2833
2834
0
  if (out_direnum)
2835
0
    *out_direnum = g_steal_pointer (&ret_direnum);
2836
0
  return TRUE;
2837
0
}
2838
2839
static gboolean
2840
add_remotes_from_keyfile (OstreeRepo *self, GKeyFile *keyfile, GFile *file, GError **error)
2841
228
{
2842
228
  GQueue queue = G_QUEUE_INIT;
2843
228
  g_auto (GStrv) groups = NULL;
2844
228
  gsize length, ii;
2845
228
  gboolean ret = FALSE;
2846
2847
228
  g_mutex_lock (&self->remotes_lock);
2848
2849
228
  groups = g_key_file_get_groups (keyfile, &length);
2850
2851
542
  for (ii = 0; ii < length; ii++)
2852
314
    {
2853
314
      OstreeRemote *remote;
2854
2855
314
      remote = ostree_remote_new_from_keyfile (keyfile, groups[ii]);
2856
2857
314
      if (remote != NULL)
2858
7
        {
2859
          /* Make sure all the remotes in the key file are
2860
           * acceptable before adding any to the OstreeRepo. */
2861
7
          g_queue_push_tail (&queue, remote);
2862
2863
7
          if (g_hash_table_contains (self->remotes, remote->name))
2864
0
            {
2865
0
              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2866
0
                           "Multiple specifications found for remote \"%s\"", remote->name);
2867
0
              goto out;
2868
0
            }
2869
2870
7
          if (file != NULL)
2871
0
            remote->file = g_object_ref (file);
2872
7
        }
2873
314
    }
2874
2875
235
  while (!g_queue_is_empty (&queue))
2876
7
    {
2877
7
      OstreeRemote *remote = g_queue_pop_head (&queue);
2878
7
      g_hash_table_replace (self->remotes, remote->name, remote);
2879
7
    }
2880
2881
228
  ret = TRUE;
2882
2883
228
out:
2884
228
  while (!g_queue_is_empty (&queue))
2885
0
    ostree_remote_unref (g_queue_pop_head (&queue));
2886
2887
228
  g_mutex_unlock (&self->remotes_lock);
2888
2889
228
  return ret;
2890
228
}
2891
2892
static gboolean
2893
append_one_remote_config (OstreeRepo *self, GFile *path, GCancellable *cancellable, GError **error)
2894
0
{
2895
0
  g_autoptr (GKeyFile) remotedata = g_key_file_new ();
2896
0
  const char *pathname = gs_file_get_path_cached (path);
2897
0
  if (!g_key_file_load_from_file (remotedata, pathname, 0, error))
2898
0
    return glnx_prefix_error (error, "Failed to parse %s", pathname);
2899
2900
0
  return add_remotes_from_keyfile (self, remotedata, path, error);
2901
0
}
2902
2903
static GFile *
2904
get_remotes_d_dir (OstreeRepo *self, GFile *sysroot)
2905
228
{
2906
228
  g_autoptr (GFile) sysroot_owned = NULL;
2907
  /* Very complicated sysroot logic; this bit breaks the otherwise mostly clean
2908
   * layering between OstreeRepo and OstreeSysroot. First, If a sysroot was
2909
   * provided, use it. Otherwise, check to see whether we reference
2910
   * /ostree/repo, or if not that, see if we have a ref to a sysroot (and it's
2911
   * physical).
2912
   */
2913
228
  g_autoptr (OstreeSysroot) sysroot_ref = NULL;
2914
228
  if (sysroot == NULL)
2915
228
    {
2916
      /* No explicit sysroot?  Let's see if we have a kind */
2917
228
      switch (self->sysroot_kind)
2918
228
        {
2919
0
        case OSTREE_REPO_SYSROOT_KIND_UNKNOWN:
2920
0
          g_assert_not_reached ();
2921
0
          break;
2922
228
        case OSTREE_REPO_SYSROOT_KIND_NO:
2923
228
          break;
2924
0
        case OSTREE_REPO_SYSROOT_KIND_IS_SYSROOT_OSTREE:
2925
0
          sysroot = sysroot_owned = g_file_new_for_path ("/");
2926
0
          break;
2927
0
        case OSTREE_REPO_SYSROOT_KIND_VIA_SYSROOT:
2928
0
          sysroot_ref = (OstreeSysroot *)g_weak_ref_get (&self->sysroot);
2929
          /* Only write to /etc/ostree/remotes.d if we are pointed at a deployment */
2930
0
          if (sysroot_ref != NULL && !sysroot_ref->is_physical)
2931
0
            sysroot = ostree_sysroot_get_path (sysroot_ref);
2932
0
          break;
2933
228
        }
2934
228
    }
2935
2936
228
  (void)sysroot_owned; // Conditionally owned
2937
2938
  /* For backwards compat, also fall back to the sysroot-path variable, which we
2939
   * don't set anymore internally, and I hope no one else uses.
2940
   */
2941
228
  if (sysroot == NULL && sysroot_ref == NULL)
2942
228
    sysroot = self->sysroot_dir;
2943
2944
  /* Was the config directory specified? If so, use that with the
2945
   * optional sysroot prepended. If not, return the path in /etc if the
2946
   * sysroot was found and NULL otherwise to use the repo config.
2947
   */
2948
228
  if (self->remotes_config_dir != NULL)
2949
0
    {
2950
0
      if (sysroot == NULL)
2951
0
        return g_file_new_for_path (self->remotes_config_dir);
2952
0
      else
2953
0
        return g_file_resolve_relative_path (sysroot, self->remotes_config_dir);
2954
0
    }
2955
228
  else if (sysroot == NULL)
2956
228
    return NULL;
2957
0
  else
2958
0
    return g_file_resolve_relative_path (sysroot, SYSCONF_REMOTES);
2959
228
}
2960
2961
static gboolean
2962
min_free_space_calculate_reserved_bytes (OstreeRepo *self, guint64 *bytes, GError **error)
2963
89
{
2964
89
  guint64 reserved_bytes = 0;
2965
2966
89
  struct statvfs stvfsbuf;
2967
89
  if (TEMP_FAILURE_RETRY (fstatvfs (self->repo_dir_fd, &stvfsbuf)) < 0)
2968
0
    return glnx_throw_errno_prefix (error, "fstatvfs");
2969
2970
89
  if (self->min_free_space_mb > 0)
2971
0
    {
2972
0
      if (self->min_free_space_mb > (G_MAXUINT64 >> 20))
2973
0
        return glnx_throw (
2974
0
            error,
2975
0
            "min-free-space value is greater than the maximum allowed value of %" G_GUINT64_FORMAT
2976
0
            " bytes",
2977
0
            (G_MAXUINT64 >> 20));
2978
2979
0
      reserved_bytes = self->min_free_space_mb << 20;
2980
0
    }
2981
89
  else if (self->min_free_space_percent > 0)
2982
89
    {
2983
89
      if (stvfsbuf.f_frsize > (G_MAXUINT64 / stvfsbuf.f_blocks))
2984
0
        return glnx_throw (
2985
0
            error,
2986
0
            "Filesystem's size is greater than the maximum allowed value of %" G_GUINT64_FORMAT
2987
0
            " bytes",
2988
0
            (G_MAXUINT64 / stvfsbuf.f_blocks));
2989
2990
89
      guint64 total_bytes = (stvfsbuf.f_frsize * stvfsbuf.f_blocks);
2991
89
      reserved_bytes = ((double)total_bytes) * (self->min_free_space_percent / 100.0);
2992
89
    }
2993
2994
89
  *bytes = reserved_bytes;
2995
89
  return TRUE;
2996
89
}
2997
2998
static gboolean
2999
min_free_space_size_validate_and_convert (OstreeRepo *self, const char *min_free_space_size_str,
3000
                                          GError **error)
3001
0
{
3002
0
  static GRegex *regex;
3003
0
  static gsize regex_initialized;
3004
0
  if (g_once_init_enter (&regex_initialized))
3005
0
    {
3006
0
      regex = g_regex_new ("^([0-9]+)(G|M|T)B$", 0, 0, NULL);
3007
0
      g_assert (regex);
3008
0
      g_once_init_leave (&regex_initialized, 1);
3009
0
    }
3010
3011
0
  g_autoptr (GMatchInfo) match = NULL;
3012
0
  if (!g_regex_match (regex, min_free_space_size_str, 0, &match))
3013
0
    return glnx_throw (error, "It should be of the format '123MB', '123GB' or '123TB'");
3014
3015
0
  g_autofree char *size_str = g_match_info_fetch (match, 1);
3016
0
  g_autofree char *unit = g_match_info_fetch (match, 2);
3017
0
  guint shifts;
3018
3019
0
  switch (*unit)
3020
0
    {
3021
0
    case 'M':
3022
0
      shifts = 0;
3023
0
      break;
3024
0
    case 'G':
3025
0
      shifts = 10;
3026
0
      break;
3027
0
    case 'T':
3028
0
      shifts = 20;
3029
0
      break;
3030
0
    default:
3031
0
      g_assert_not_reached ();
3032
0
    }
3033
3034
0
  guint64 min_free_space = g_ascii_strtoull (size_str, NULL, 10);
3035
0
  if (shifts > 0 && g_bit_nth_lsf (min_free_space, 63 - shifts) != -1)
3036
0
    return glnx_throw (error, "Value was too high");
3037
3038
0
  self->min_free_space_mb = min_free_space << shifts;
3039
3040
0
  return TRUE;
3041
0
}
3042
3043
static gboolean
3044
reload_core_config (OstreeRepo *self, GCancellable *cancellable, GError **error)
3045
228
{
3046
228
  g_autofree char *version = NULL;
3047
228
  g_autofree char *mode = NULL;
3048
228
  g_autofree char *contents = NULL;
3049
228
  g_autofree char *parent_repo_path = NULL;
3050
228
  gboolean is_archive;
3051
3052
228
  version = g_key_file_get_value (self->config, "core", "repo_version", error);
3053
228
  if (!version)
3054
0
    return FALSE;
3055
3056
228
  if (strcmp (version, "1") != 0)
3057
0
    return glnx_throw (error, "Invalid repository version '%s'", version);
3058
3059
228
  if (!ot_keyfile_get_boolean_with_default (self->config, "core", "archive", FALSE, &is_archive,
3060
228
                                            error))
3061
0
    return FALSE;
3062
228
  if (is_archive)
3063
0
    {
3064
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
3065
0
                   "This version of OSTree no longer supports \"archive\" repositories; use "
3066
0
                   "archive-z2 instead");
3067
0
      return FALSE;
3068
0
    }
3069
3070
228
  if (!ot_keyfile_get_value_with_default (self->config, "core", "mode", "bare", &mode, error))
3071
0
    return FALSE;
3072
228
  if (!ostree_repo_mode_from_string (mode, &self->mode, error))
3073
0
    return FALSE;
3074
3075
228
  if (self->writable)
3076
228
    {
3077
228
      if (!ot_keyfile_get_boolean_with_default (self->config, "core", "enable-uncompressed-cache",
3078
228
                                                TRUE, &self->enable_uncompressed_cache, error))
3079
0
        return FALSE;
3080
228
    }
3081
0
  else
3082
0
    self->enable_uncompressed_cache = FALSE;
3083
3084
228
  {
3085
228
    gboolean do_fsync;
3086
3087
228
    if (!ot_keyfile_get_boolean_with_default (self->config, "core", "fsync", TRUE, &do_fsync,
3088
228
                                              error))
3089
0
      return FALSE;
3090
3091
228
    if (!do_fsync)
3092
0
      ostree_repo_set_disable_fsync (self, TRUE);
3093
228
  }
3094
3095
228
  if (!ot_keyfile_get_boolean_with_default (self->config, "core", "per-object-fsync", FALSE,
3096
228
                                            &self->per_object_fsync, error))
3097
0
    return FALSE;
3098
3099
  /* See https://github.com/ostreedev/ostree/issues/758 */
3100
228
  if (!ot_keyfile_get_boolean_with_default (self->config, "core", "disable-xattrs", FALSE,
3101
228
                                            &self->disable_xattrs, error))
3102
0
    return FALSE;
3103
3104
228
  {
3105
228
    g_autofree char *tmp_expiry_seconds = NULL;
3106
3107
    /* 86400 secs = one day */
3108
228
    if (!ot_keyfile_get_value_with_default (self->config, "core", "tmp-expiry-secs", "86400",
3109
228
                                            &tmp_expiry_seconds, error))
3110
0
      return FALSE;
3111
3112
228
    self->tmp_expiry_seconds = g_ascii_strtoull (tmp_expiry_seconds, NULL, 10);
3113
228
  }
3114
3115
0
  {
3116
228
    gboolean locking;
3117
    /* Enabled by default in 2018.05 */
3118
228
    if (!ot_keyfile_get_boolean_with_default (self->config, "core", "locking", TRUE, &locking,
3119
228
                                              error))
3120
0
      return FALSE;
3121
228
    if (!locking)
3122
0
      {
3123
0
        self->lock_timeout_seconds = REPO_LOCK_DISABLED;
3124
0
      }
3125
228
    else
3126
228
      {
3127
228
        g_autofree char *lock_timeout_seconds = NULL;
3128
3129
228
        if (!ot_keyfile_get_value_with_default (self->config, "core", "lock-timeout-secs", "300",
3130
228
                                                &lock_timeout_seconds, error))
3131
0
          return FALSE;
3132
3133
228
        self->lock_timeout_seconds = g_ascii_strtoll (lock_timeout_seconds, NULL, 10);
3134
228
      }
3135
228
  }
3136
3137
228
  {
3138
228
    g_autofree char *compression_level_str = NULL;
3139
3140
    /* gzip defaults to 6 */
3141
228
    (void)ot_keyfile_get_value_with_default (self->config, "archive", "zlib-level", NULL,
3142
228
                                             &compression_level_str, NULL);
3143
3144
228
    if (compression_level_str)
3145
      /* Ensure level is in [1,9] */
3146
0
      self->zlib_compression_level
3147
0
          = MAX (1, MIN (9, g_ascii_strtoull (compression_level_str, NULL, 10)));
3148
228
    else
3149
228
      self->zlib_compression_level = OSTREE_ARCHIVE_DEFAULT_COMPRESSION_LEVEL;
3150
228
  }
3151
3152
228
  {
3153
    /* Try to parse both min-free-space-* config options first. If both are absent, fallback on 3%
3154
     * free space. If both are present and are non-zero, use min-free-space-size unconditionally
3155
     * and display a warning.
3156
     */
3157
228
    if (g_key_file_has_key (self->config, "core", "min-free-space-size", NULL))
3158
0
      {
3159
0
        g_autofree char *min_free_space_size_str = NULL;
3160
3161
0
        if (!ot_keyfile_get_value_with_default (self->config, "core", "min-free-space-size", NULL,
3162
0
                                                &min_free_space_size_str, error))
3163
0
          return FALSE;
3164
3165
        /* Validate the string and convert the size to MBs */
3166
0
        if (!min_free_space_size_validate_and_convert (self, min_free_space_size_str, error))
3167
0
          return glnx_prefix_error (error, "Invalid min-free-space-size '%s'",
3168
0
                                    min_free_space_size_str);
3169
0
      }
3170
3171
228
    if (g_key_file_has_key (self->config, "core", "min-free-space-percent", NULL))
3172
0
      {
3173
0
        g_autofree char *min_free_space_percent_str = NULL;
3174
3175
0
        if (!ot_keyfile_get_value_with_default (self->config, "core", "min-free-space-percent",
3176
0
                                                NULL, &min_free_space_percent_str, error))
3177
0
          return FALSE;
3178
3179
0
        self->min_free_space_percent = g_ascii_strtoull (min_free_space_percent_str, NULL, 10);
3180
0
        if (self->min_free_space_percent > 99)
3181
0
          return glnx_throw (error, "Invalid min-free-space-percent '%s'",
3182
0
                             min_free_space_percent_str);
3183
0
      }
3184
228
    else if (!g_key_file_has_key (self->config, "core", "min-free-space-size", NULL))
3185
228
      {
3186
        /* Default fallback of 3% free space. If changing this, be sure to change the man page too
3187
         */
3188
228
        self->min_free_space_percent = 3;
3189
228
        self->min_free_space_mb = 0;
3190
228
      }
3191
3192
228
    if (self->min_free_space_percent != 0 && self->min_free_space_mb != 0)
3193
0
      {
3194
0
        self->min_free_space_percent = 0;
3195
0
        g_debug ("Both min-free-space-percent and -size are mentioned in config. Enforcing "
3196
0
                 "min-free-space-size check only.");
3197
0
      }
3198
228
  }
3199
3200
228
  if (!_ostree_repo_parse_fsverity_config (self, error))
3201
0
    return FALSE;
3202
3203
228
  if (!_ostree_repo_parse_composefs_config (self, error))
3204
0
    return FALSE;
3205
3206
228
  {
3207
228
    g_clear_pointer (&self->collection_id, g_free);
3208
228
    if (!ot_keyfile_get_value_with_default (self->config, "core", "collection-id", NULL,
3209
228
                                            &self->collection_id, NULL))
3210
0
      return FALSE;
3211
228
  }
3212
3213
228
  if (!ot_keyfile_get_value_with_default (self->config, "core", "parent", NULL, &parent_repo_path,
3214
228
                                          error))
3215
0
    return FALSE;
3216
3217
228
  if (parent_repo_path && parent_repo_path[0])
3218
0
    {
3219
0
      g_autoptr (GFile) parent_repo_f = g_file_new_for_path (parent_repo_path);
3220
3221
0
      g_clear_object (&self->parent_repo);
3222
0
      self->parent_repo = ostree_repo_new (parent_repo_f);
3223
3224
0
      if (!ostree_repo_open (self->parent_repo, cancellable, error))
3225
0
        {
3226
0
          g_prefix_error (error, "While checking parent repository '%s': ",
3227
0
                          gs_file_get_path_cached (parent_repo_f));
3228
0
          return FALSE;
3229
0
        }
3230
0
    }
3231
3232
  /* By default, only add remotes in a remotes config directory for
3233
   * system repos. This is to preserve legacy behavior for non-system
3234
   * repos that specify a remotes config dir (flatpak).
3235
   */
3236
228
  {
3237
228
    gboolean is_system = ostree_repo_is_system (self);
3238
3239
228
    if (!ot_keyfile_get_boolean_with_default (self->config, "core", "add-remotes-config-dir",
3240
228
                                              is_system, &self->add_remotes_config_dir, error))
3241
0
      return FALSE;
3242
228
  }
3243
3244
228
  {
3245
228
    g_autofree char *payload_threshold = NULL;
3246
3247
228
    if (!ot_keyfile_get_value_with_default (self->config, "core", "payload-link-threshold", "-1",
3248
228
                                            &payload_threshold, error))
3249
0
      return FALSE;
3250
3251
228
    self->payload_link_threshold = g_ascii_strtoull (payload_threshold, NULL, 10);
3252
228
  }
3253
3254
0
  {
3255
228
    g_auto (GStrv) configured_finders = NULL;
3256
228
    g_autoptr (GError) local_error = NULL;
3257
3258
228
    configured_finders = g_key_file_get_string_list (self->config, "core", "default-repo-finders",
3259
228
                                                     NULL, &local_error);
3260
228
    if (g_error_matches (local_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
3261
228
      g_clear_error (&local_error);
3262
0
    else if (local_error != NULL)
3263
0
      {
3264
0
        g_propagate_error (error, g_steal_pointer (&local_error));
3265
0
        return FALSE;
3266
0
      }
3267
3268
228
    if (configured_finders != NULL && *configured_finders == NULL)
3269
0
      return glnx_throw (error, "Invalid empty default-repo-finders configuration");
3270
3271
228
    for (char **iter = configured_finders; iter && *iter; iter++)
3272
0
      {
3273
0
        const char *repo_finder = *iter;
3274
3275
0
        if (strcmp (repo_finder, "config") != 0 && strcmp (repo_finder, "lan") != 0
3276
0
            && strcmp (repo_finder, "mount") != 0)
3277
0
          return glnx_throw (error, "Invalid configured repo-finder '%s'", repo_finder);
3278
0
      }
3279
3280
    /* Fall back to a default set of finders */
3281
228
    if (configured_finders == NULL)
3282
228
      configured_finders = g_strsplit ("config;mount", ";", -1);
3283
3284
228
    g_clear_pointer (&self->repo_finders, g_strfreev);
3285
228
    self->repo_finders = g_steal_pointer (&configured_finders);
3286
228
  }
3287
3288
228
  return TRUE;
3289
228
}
3290
3291
static gboolean
3292
reload_remote_config (OstreeRepo *self, GCancellable *cancellable, GError **error)
3293
228
{
3294
228
  GLNX_AUTO_PREFIX_ERROR ("Reloading remotes", error);
3295
3296
228
  g_mutex_lock (&self->remotes_lock);
3297
228
  g_hash_table_remove_all (self->remotes);
3298
228
  g_mutex_unlock (&self->remotes_lock);
3299
3300
228
  if (!add_remotes_from_keyfile (self, self->config, NULL, error))
3301
0
    return FALSE;
3302
3303
228
  g_autoptr (GFile) remotes_d = get_remotes_d_dir (self, NULL);
3304
228
  if (remotes_d == NULL)
3305
228
    return TRUE;
3306
3307
0
  g_autoptr (GFileEnumerator) direnum = NULL;
3308
0
  if (!enumerate_directory_allow_noent (remotes_d, OSTREE_GIO_FAST_QUERYINFO, 0, &direnum,
3309
0
                                        cancellable, error))
3310
0
    return FALSE;
3311
0
  if (direnum)
3312
0
    {
3313
0
      while (TRUE)
3314
0
        {
3315
0
          GFileInfo *file_info;
3316
0
          GFile *path;
3317
0
          const char *name;
3318
0
          guint32 type;
3319
3320
0
          if (!g_file_enumerator_iterate (direnum, &file_info, &path, NULL, error))
3321
0
            return FALSE;
3322
0
          if (file_info == NULL)
3323
0
            break;
3324
3325
0
          name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
3326
0
          type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
3327
3328
0
          if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".conf"))
3329
0
            {
3330
0
              if (!append_one_remote_config (self, path, cancellable, error))
3331
0
                return FALSE;
3332
0
            }
3333
0
        }
3334
0
    }
3335
3336
0
  return TRUE;
3337
0
}
3338
3339
static gboolean
3340
reload_sysroot_config (OstreeRepo *self, GCancellable *cancellable, GError **error)
3341
228
{
3342
228
  g_autofree char *boot_counting_str = NULL;
3343
3344
228
  if (!ot_keyfile_get_value_with_default_group_optional (
3345
228
          self->config, "sysroot", "boot-counting-tries", "0", &boot_counting_str, error))
3346
0
    return FALSE;
3347
228
  guint64 v;
3348
228
  if (!g_ascii_string_to_unsigned (boot_counting_str, 10, 0, 5, &v, error))
3349
0
    return glnx_prefix_error (error, "Parsing sysroot.boot-counting-tries");
3350
228
  self->boot_counting = (guint)v;
3351
3352
228
  g_autofree char *bootloader = NULL;
3353
3354
228
  if (!ot_keyfile_get_value_with_default_group_optional (self->config, "sysroot", "bootloader",
3355
228
                                                         CFG_SYSROOT_BOOTLOADER_DEFAULT_STR,
3356
228
                                                         &bootloader, error))
3357
0
    return FALSE;
3358
3359
  /* TODO: possibly later add support for specifying a generic bootloader
3360
   * binary "x" in /usr/lib/ostree/bootloaders/x). See:
3361
   * https://github.com/ostreedev/ostree/issues/1719
3362
   * https://github.com/ostreedev/ostree/issues/1801
3363
   */
3364
228
  gboolean valid_bootloader = FALSE;
3365
1.82k
  for (int i = 0; CFG_SYSROOT_BOOTLOADER_OPTS_STR[i]; i++)
3366
1.59k
    {
3367
1.59k
      if (g_str_equal (bootloader, CFG_SYSROOT_BOOTLOADER_OPTS_STR[i]))
3368
228
        {
3369
228
          self->bootloader = (OstreeCfgSysrootBootloaderOpt)i;
3370
228
          valid_bootloader = TRUE;
3371
228
        }
3372
1.59k
    }
3373
228
  if (!valid_bootloader)
3374
0
    {
3375
0
      return glnx_throw (error, "Invalid bootloader configuration: '%s'", bootloader);
3376
0
    }
3377
  /* Parse bls-append-except-default string list. */
3378
228
  g_auto (GStrv) read_values = NULL;
3379
228
  if (!ot_keyfile_get_string_list_with_default (
3380
228
          self->config, "sysroot", "bls-append-except-default", ';', NULL, &read_values, error))
3381
0
    return glnx_throw (error, "Unable to parse bls-append-except-default");
3382
3383
  /* get all key value pairs in bls-append-except-default */
3384
228
  g_hash_table_remove_all (self->bls_append_values);
3385
228
  for (char **iter = read_values; iter && *iter; iter++)
3386
0
    {
3387
0
      const char *key_value = *iter;
3388
0
      const char *sep = strchr (key_value, '=');
3389
0
      if (sep == NULL)
3390
0
        {
3391
0
          glnx_throw (
3392
0
              error,
3393
0
              "bls-append-except-default key must be of the form \"key1=value1;key2=value2...\"");
3394
0
          return FALSE;
3395
0
        }
3396
0
      char *key = g_strndup (key_value, sep - key_value);
3397
0
      char *value = g_strdup (sep + 1);
3398
0
      g_hash_table_replace (self->bls_append_values, key, value);
3399
0
    }
3400
3401
228
  if (!ot_keyfile_get_boolean_with_default (self->config, "sysroot", "bootprefix", FALSE,
3402
228
                                            &self->enable_bootprefix, error))
3403
0
    return FALSE;
3404
3405
228
  return TRUE;
3406
228
}
3407
3408
static gboolean
3409
reload_config_inner (OstreeRepo *self, GCancellable *cancellable, GError **error)
3410
228
{
3411
228
  if (!reload_core_config (self, cancellable, error))
3412
0
    return FALSE;
3413
228
  if (!reload_remote_config (self, cancellable, error))
3414
0
    return FALSE;
3415
228
  if (!reload_sysroot_config (self, cancellable, error))
3416
0
    return FALSE;
3417
228
  return TRUE;
3418
228
}
3419
3420
/**
3421
 * ostree_repo_reload_config:
3422
 * @self: repo
3423
 * @cancellable: cancellable
3424
 * @error: error
3425
 *
3426
 * By default, an #OstreeRepo will cache the remote configuration and its
3427
 * own repo/config data.  This API can be used to reload it.
3428
 *
3429
 * Since: 2017.2
3430
 */
3431
gboolean
3432
ostree_repo_reload_config (OstreeRepo *self, GCancellable *cancellable, GError **error)
3433
278
{
3434
278
  g_clear_pointer (&self->config, g_key_file_unref);
3435
278
  self->config = g_key_file_new ();
3436
3437
278
  gsize len;
3438
278
  g_autofree char *contents
3439
278
      = glnx_file_get_contents_utf8_at (self->repo_dir_fd, "config", &len, NULL, error);
3440
278
  if (!contents)
3441
50
    return FALSE;
3442
228
  if (!g_key_file_load_from_data (self->config, contents, len, 0, error))
3443
0
    {
3444
0
      g_prefix_error (error, "Couldn't parse config file: ");
3445
0
      return FALSE;
3446
0
    }
3447
3448
228
  return reload_config_inner (self, cancellable, error);
3449
228
}
3450
3451
gboolean
3452
ostree_repo_open (OstreeRepo *self, GCancellable *cancellable, GError **error)
3453
139
{
3454
139
  GLNX_AUTO_PREFIX_ERROR ("opening repo", error);
3455
3456
139
  struct stat stbuf;
3457
3458
139
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3459
3460
139
  if (self->inited)
3461
0
    return TRUE;
3462
3463
  /* We use a directory of the form `staging-${BOOT_ID}-${RANDOM}`
3464
   * where if the ${BOOT_ID} doesn't match, we know file contents
3465
   * possibly haven't been sync'd to disk and need to be discarded.
3466
   */
3467
139
  {
3468
139
    const char *env_bootid = getenv ("OSTREE_BOOTID");
3469
139
    g_autofree char *boot_id = NULL;
3470
3471
139
    if (env_bootid != NULL)
3472
0
      boot_id = g_strdup (env_bootid);
3473
139
    else
3474
139
      {
3475
139
        if (!g_file_get_contents ("/proc/sys/kernel/random/boot_id", &boot_id, NULL, error))
3476
0
          return FALSE;
3477
139
        g_strdelimit (boot_id, "\n", '\0');
3478
139
      }
3479
3480
139
    self->stagedir_prefix = g_strconcat (OSTREE_REPO_TMPDIR_STAGING, boot_id, "-", NULL);
3481
139
  }
3482
3483
139
  if (self->repo_dir_fd == -1)
3484
0
    {
3485
0
      g_assert (self->repodir);
3486
0
      if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (self->repodir), TRUE,
3487
0
                           &self->repo_dir_fd, error))
3488
0
        return FALSE;
3489
0
    }
3490
3491
139
  if (!glnx_fstat (self->repo_dir_fd, &stbuf, error))
3492
0
    return FALSE;
3493
139
  self->device = stbuf.st_dev;
3494
139
  self->inode = stbuf.st_ino;
3495
3496
139
  if (!glnx_opendirat (self->repo_dir_fd, "objects", TRUE, &self->objects_dir_fd, error))
3497
0
    return FALSE;
3498
3499
139
  self->writable = faccessat (self->objects_dir_fd, ".", W_OK, 0) == 0;
3500
139
  if (!self->writable)
3501
0
    {
3502
      /* This is returned through ostree_repo_is_writable(). */
3503
0
      glnx_set_error_from_errno (&self->writable_error);
3504
      /* Note - we don't return this error yet! */
3505
0
    }
3506
3507
139
  {
3508
139
    struct statfs fsstbuf;
3509
139
    if (fstatfs (self->repo_dir_fd, &fsstbuf) < 0)
3510
0
      return glnx_throw_errno_prefix (error, "fstatfs");
3511
139
#ifndef FUSE_SUPER_MAGIC
3512
139
#define FUSE_SUPER_MAGIC 0x65735546
3513
139
#endif
3514
139
    self->is_on_fuse = (fsstbuf.f_type == FUSE_SUPER_MAGIC);
3515
139
    g_debug ("using fuse: %d", self->is_on_fuse);
3516
139
  }
3517
3518
139
  if (!glnx_fstat (self->objects_dir_fd, &stbuf, error))
3519
0
    return FALSE;
3520
139
  self->owner_uid = stbuf.st_uid;
3521
3522
139
  if (self->writable)
3523
139
    {
3524
      /* Always try to recreate the tmpdir to be nice to people
3525
       * who are looking to free up space.
3526
       *
3527
       * https://github.com/ostreedev/ostree/issues/1018
3528
       */
3529
139
      if (mkdirat (self->repo_dir_fd, "tmp", DEFAULT_DIRECTORY_MODE) == -1)
3530
139
        {
3531
139
          if (G_UNLIKELY (errno != EEXIST))
3532
0
            return glnx_throw_errno_prefix (error, "mkdir(tmp)");
3533
139
        }
3534
139
    }
3535
3536
139
  if (!glnx_opendirat (self->repo_dir_fd, "tmp", TRUE, &self->tmp_dir_fd, error))
3537
0
    return FALSE;
3538
3539
139
  if (self->writable && getenv ("OSTREE_SKIP_CACHE") == NULL)
3540
139
    {
3541
139
      if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, DEFAULT_DIRECTORY_MODE,
3542
139
                                   cancellable, error))
3543
0
        return FALSE;
3544
3545
139
      if (!glnx_opendirat (self->tmp_dir_fd, _OSTREE_CACHE_DIR, TRUE, &self->cache_dir_fd, error))
3546
0
        return FALSE;
3547
139
    }
3548
3549
  /* If we weren't created via ostree_sysroot_get_repo(), for backwards
3550
   * compatibility we need to figure out now whether or not we refer to the
3551
   * system repo.  See also ostree-sysroot.c.
3552
   */
3553
139
  if (self->sysroot_kind == OSTREE_REPO_SYSROOT_KIND_UNKNOWN)
3554
139
    {
3555
139
      struct stat system_stbuf;
3556
      /* Ignore any errors if we can't access /ostree/repo */
3557
139
      if (fstatat (AT_FDCWD, "/ostree/repo", &system_stbuf, 0) == 0)
3558
0
        {
3559
          /* Are we the same as /ostree/repo? */
3560
0
          if (self->device == system_stbuf.st_dev && self->inode == system_stbuf.st_ino)
3561
0
            self->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_IS_SYSROOT_OSTREE;
3562
0
          else
3563
0
            self->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_NO;
3564
0
        }
3565
139
      else
3566
139
        self->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_NO;
3567
139
    }
3568
3569
139
  if (!ostree_repo_reload_config (self, cancellable, error))
3570
0
    return FALSE;
3571
3572
139
  self->inited = TRUE;
3573
139
  return TRUE;
3574
139
}
3575
3576
/**
3577
 * ostree_repo_set_disable_fsync:
3578
 * @self: An #OstreeRepo
3579
 * @disable_fsync: If %TRUE, do not fsync
3580
 *
3581
 * Disable requests to fsync() to stable storage during commits.  This
3582
 * option should only be used by build system tools which are creating
3583
 * disposable virtual machines, or have higher level mechanisms for
3584
 * ensuring data consistency.
3585
 */
3586
void
3587
ostree_repo_set_disable_fsync (OstreeRepo *self, gboolean disable_fsync)
3588
0
{
3589
0
  self->disable_fsync = disable_fsync;
3590
0
}
3591
3592
/**
3593
 * ostree_repo_set_cache_dir:
3594
 * @self: An #OstreeRepo
3595
 * @dfd: directory fd
3596
 * @path: subpath in @dfd
3597
 * @cancellable: a #GCancellable
3598
 * @error: a #GError
3599
 *
3600
 * Set a custom location for the cache directory used for e.g.
3601
 * per-remote summary caches. Setting this manually is useful when
3602
 * doing operations on a system repo as a user because you don't have
3603
 * write permissions in the repo, where the cache is normally stored.
3604
 *
3605
 * Since: 2016.5
3606
 */
3607
gboolean
3608
ostree_repo_set_cache_dir (OstreeRepo *self, int dfd, const char *path, GCancellable *cancellable,
3609
                           GError **error)
3610
0
{
3611
0
  glnx_autofd int fd = -1;
3612
0
  if (!glnx_opendirat (dfd, path, TRUE, &fd, error))
3613
0
    return FALSE;
3614
3615
0
  glnx_close_fd (&self->cache_dir_fd);
3616
0
  self->cache_dir_fd = g_steal_fd (&fd);
3617
3618
0
  return TRUE;
3619
0
}
3620
3621
/**
3622
 * ostree_repo_get_disable_fsync:
3623
 * @self: An #OstreeRepo
3624
 *
3625
 * For more information see ostree_repo_set_disable_fsync().
3626
 *
3627
 * Returns: Whether or not fsync() is enabled for this repo.
3628
 */
3629
gboolean
3630
ostree_repo_get_disable_fsync (OstreeRepo *self)
3631
0
{
3632
0
  return self->disable_fsync;
3633
0
}
3634
3635
/* Replace the contents of a file, honoring the repository's fsync
3636
 * policy.
3637
 */
3638
gboolean
3639
_ostree_repo_file_replace_contents (OstreeRepo *self, int dfd, const char *path, const guint8 *buf,
3640
                                    gsize len, GCancellable *cancellable, GError **error)
3641
0
{
3642
0
  return glnx_file_replace_contents_at (dfd, path, buf, len,
3643
0
                                        self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC
3644
0
                                                            : GLNX_FILE_REPLACE_DATASYNC_NEW,
3645
0
                                        cancellable, error);
3646
0
}
3647
3648
/**
3649
 * ostree_repo_get_path:
3650
 * @self: Repo
3651
 *
3652
 * Note that since the introduction of ostree_repo_open_at(), this function may
3653
 * return a process-specific path in `/proc` if the repository was created using
3654
 * that API. In general, you should avoid use of this API.
3655
 *
3656
 * Returns: (transfer none): Path to repo
3657
 */
3658
GFile *
3659
ostree_repo_get_path (OstreeRepo *self)
3660
0
{
3661
  /* Did we have an abspath?  Return it */
3662
0
  if (self->repodir)
3663
0
    return self->repodir;
3664
  /* Lazily create a fd-relative path */
3665
0
  if (!self->repodir_fdrel)
3666
0
    self->repodir_fdrel = ot_fdrel_to_gfile (self->repo_dir_fd, ".");
3667
0
  return self->repodir_fdrel;
3668
0
}
3669
3670
/**
3671
 * ostree_repo_get_dfd:
3672
 * @self: Repo
3673
 *
3674
 * In some cases it's useful for applications to access the repository
3675
 * directly; for example, writing content into `repo/tmp` ensures it's
3676
 * on the same filesystem.  Another case is detecting the mtime on the
3677
 * repository (to see whether a ref was written).
3678
 *
3679
 * Returns: File descriptor for repository root - owned by @self
3680
 * Since: 2016.4
3681
 */
3682
int
3683
ostree_repo_get_dfd (OstreeRepo *self)
3684
0
{
3685
0
  g_return_val_if_fail (self->repo_dir_fd != -1, -1);
3686
0
  return self->repo_dir_fd;
3687
0
}
3688
3689
/**
3690
 * ostree_repo_hash:
3691
 * @self: an #OstreeRepo
3692
 *
3693
 * Calculate a hash value for the given open repository, suitable for use when
3694
 * putting it into a hash table. It is an error to call this on an #OstreeRepo
3695
 * which is not yet open, as a persistent hash value cannot be calculated until
3696
 * the repository is open and the inode of its root directory has been loaded.
3697
 *
3698
 * This function does no I/O.
3699
 *
3700
 * Returns: hash value for the #OstreeRepo
3701
 * Since: 2017.12
3702
 */
3703
guint
3704
ostree_repo_hash (OstreeRepo *self)
3705
0
{
3706
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), 0);
3707
3708
  /* We cannot hash non-open repositories, since their hash value would change
3709
   * once they’re opened, resulting in false lookup misses and the inability to
3710
   * remove them from a hash table. */
3711
0
  g_assert (self->repo_dir_fd >= 0);
3712
3713
  /* device and inode numbers are distributed fairly uniformly, so we can’t
3714
   * do much better than just combining them. No need to rehash to even out
3715
   * the distribution. */
3716
0
  return (self->device ^ self->inode);
3717
0
}
3718
3719
/**
3720
 * ostree_repo_equal:
3721
 * @a: an #OstreeRepo
3722
 * @b: an #OstreeRepo
3723
 *
3724
 * Check whether two opened repositories are the same on disk: if their root
3725
 * directories are the same inode. If @a or @b are not open yet (due to
3726
 * ostree_repo_open() not being called on them yet), %FALSE will be returned.
3727
 *
3728
 * Returns: %TRUE if @a and @b are the same repository on disk, %FALSE otherwise
3729
 * Since: 2017.12
3730
 */
3731
gboolean
3732
ostree_repo_equal (OstreeRepo *a, OstreeRepo *b)
3733
0
{
3734
0
  g_return_val_if_fail (OSTREE_IS_REPO (a), FALSE);
3735
0
  g_return_val_if_fail (OSTREE_IS_REPO (b), FALSE);
3736
3737
0
  if (a->repo_dir_fd < 0 || b->repo_dir_fd < 0)
3738
0
    return FALSE;
3739
3740
0
  return (a->device == b->device && a->inode == b->inode);
3741
0
}
3742
3743
OstreeRepoMode
3744
ostree_repo_get_mode (OstreeRepo *self)
3745
0
{
3746
0
  g_assert (self != NULL);
3747
0
  g_assert (self->inited);
3748
3749
0
  return self->mode;
3750
0
}
3751
3752
/**
3753
 * ostree_repo_get_min_free_space_bytes:
3754
 * @self: Repo
3755
 * @out_reserved_bytes: (out): Location to store the result
3756
 * @error: Return location for a #GError
3757
 *
3758
 * Determine the number of bytes of free disk space that are reserved according
3759
 * to the repo config and return that number in @out_reserved_bytes. See the
3760
 * documentation for the core.min-free-space-size and
3761
 * core.min-free-space-percent repo config options.
3762
 *
3763
 * Returns: %TRUE on success, %FALSE otherwise.
3764
 * Since: 2018.9
3765
 */
3766
gboolean
3767
ostree_repo_get_min_free_space_bytes (OstreeRepo *self, guint64 *out_reserved_bytes, GError **error)
3768
139
{
3769
139
  g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
3770
139
  g_return_val_if_fail (out_reserved_bytes != NULL, FALSE);
3771
139
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3772
3773
89
  if (!min_free_space_calculate_reserved_bytes (self, out_reserved_bytes, error))
3774
0
    return glnx_prefix_error (error, "Error calculating min-free-space bytes");
3775
3776
89
  return TRUE;
3777
89
}
3778
3779
/**
3780
 * ostree_repo_get_parent:
3781
 * @self: Repo
3782
 *
3783
 * Before this function can be used, ostree_repo_init() must have been
3784
 * called.
3785
 *
3786
 * Returns: (transfer none) (nullable): Parent repository, or %NULL if none
3787
 */
3788
OstreeRepo *
3789
ostree_repo_get_parent (OstreeRepo *self)
3790
0
{
3791
0
  return self->parent_repo;
3792
0
}
3793
3794
static gboolean
3795
list_loose_objects_at (OstreeRepo *self, GVariant *dummy_value, GHashTable *inout_objects, int dfd,
3796
                       const char *prefix, const char *commit_starting_with,
3797
                       GCancellable *cancellable, GError **error)
3798
0
{
3799
0
  GVariant *key;
3800
3801
0
  g_auto (GLnxDirFdIterator) dfd_iter = {
3802
0
    0,
3803
0
  };
3804
0
  gboolean exists;
3805
0
  if (!ot_dfd_iter_init_allow_noent (dfd, prefix, &dfd_iter, &exists, error))
3806
0
    return FALSE;
3807
  /* Note early return */
3808
0
  if (!exists)
3809
0
    return TRUE;
3810
3811
0
  while (TRUE)
3812
0
    {
3813
0
      struct dirent *dent;
3814
3815
0
      if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
3816
0
        return FALSE;
3817
0
      if (dent == NULL)
3818
0
        break;
3819
3820
0
      const char *name = dent->d_name;
3821
0
      if (strcmp (name, ".") == 0 || strcmp (name, "..") == 0)
3822
0
        continue;
3823
3824
0
      const char *dot = strrchr (name, '.');
3825
0
      if (!dot)
3826
0
        continue;
3827
3828
0
      OstreeObjectType objtype;
3829
0
      if ((self->mode == OSTREE_REPO_MODE_ARCHIVE && strcmp (dot, ".filez") == 0)
3830
0
          || ((_ostree_repo_mode_is_bare (self->mode)) && strcmp (dot, ".file") == 0))
3831
0
        objtype = OSTREE_OBJECT_TYPE_FILE;
3832
0
      else if (strcmp (dot, ".dirtree") == 0)
3833
0
        objtype = OSTREE_OBJECT_TYPE_DIR_TREE;
3834
0
      else if (strcmp (dot, ".dirmeta") == 0)
3835
0
        objtype = OSTREE_OBJECT_TYPE_DIR_META;
3836
0
      else if (strcmp (dot, ".commit") == 0)
3837
0
        objtype = OSTREE_OBJECT_TYPE_COMMIT;
3838
0
      else if (strcmp (dot, ".payload-link") == 0)
3839
0
        objtype = OSTREE_OBJECT_TYPE_PAYLOAD_LINK;
3840
0
      else
3841
0
        continue;
3842
3843
0
      if ((dot - name) != 62)
3844
0
        continue;
3845
3846
0
      char buf[OSTREE_SHA256_STRING_LEN + 1];
3847
3848
0
      memcpy (buf, prefix, 2);
3849
0
      memcpy (buf + 2, name, 62);
3850
0
      buf[sizeof (buf) - 1] = '\0';
3851
3852
      /* if we passed in a "starting with" argument, then
3853
         we only want to return .commit objects with a checksum
3854
         that matches the commit_starting_with argument */
3855
0
      if (commit_starting_with)
3856
0
        {
3857
          /* object is not a commit, do not add to array */
3858
0
          if (objtype != OSTREE_OBJECT_TYPE_COMMIT)
3859
0
            continue;
3860
3861
          /* commit checksum does not match "starting with", do not add to array */
3862
0
          if (!g_str_has_prefix (buf, commit_starting_with))
3863
0
            continue;
3864
0
        }
3865
3866
0
      key = ostree_object_name_serialize (buf, objtype);
3867
3868
      /* transfer ownership */
3869
0
      if (dummy_value)
3870
0
        g_hash_table_replace (inout_objects, g_variant_ref_sink (key), g_variant_ref (dummy_value));
3871
0
      else
3872
0
        g_hash_table_add (inout_objects, g_variant_ref_sink (key));
3873
0
    }
3874
3875
0
  return TRUE;
3876
0
}
3877
3878
static gboolean
3879
list_loose_objects (OstreeRepo *self, GVariant *dummy_value, GHashTable *inout_objects,
3880
                    const char *commit_starting_with, GCancellable *cancellable, GError **error)
3881
0
{
3882
0
  static const gchar hexchars[] = "0123456789abcdef";
3883
3884
0
  for (guint c = 0; c < 256; c++)
3885
0
    {
3886
0
      char buf[3];
3887
0
      buf[0] = hexchars[c >> 4];
3888
0
      buf[1] = hexchars[c & 0xF];
3889
0
      buf[2] = '\0';
3890
0
      if (!list_loose_objects_at (self, dummy_value, inout_objects, self->objects_dir_fd, buf,
3891
0
                                  commit_starting_with, cancellable, error))
3892
0
        return FALSE;
3893
0
    }
3894
3895
0
  return TRUE;
3896
0
}
3897
3898
static gboolean
3899
load_metadata_internal (OstreeRepo *self, OstreeObjectType objtype, const char *sha256,
3900
                        gboolean error_if_not_found, GVariant **out_variant,
3901
                        GInputStream **out_stream, guint64 *out_size,
3902
                        OstreeRepoCommitState *out_state, GCancellable *cancellable, GError **error)
3903
0
{
3904
0
  char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
3905
0
  glnx_autofd int fd = -1;
3906
0
  g_autoptr (GInputStream) ret_stream = NULL;
3907
0
  g_autoptr (GVariant) ret_variant = NULL;
3908
3909
0
  g_return_val_if_fail (OSTREE_OBJECT_TYPE_IS_META (objtype), FALSE);
3910
0
  g_return_val_if_fail (objtype == OSTREE_OBJECT_TYPE_COMMIT || out_state == NULL, FALSE);
3911
3912
  /* Ensure this is set to NULL if we didn't find the object */
3913
0
  if (out_variant)
3914
0
    *out_variant = NULL;
3915
3916
  /* Special caching for dirmeta objects, since they're commonly referenced many
3917
   * times.
3918
   */
3919
0
  const gboolean is_dirmeta_cachable
3920
0
      = (objtype == OSTREE_OBJECT_TYPE_DIR_META && out_variant && !out_stream);
3921
0
  if (is_dirmeta_cachable)
3922
0
    {
3923
0
      GMutex *lock = &self->cache_lock;
3924
0
      g_mutex_lock (lock);
3925
0
      GVariant *cache_hit = NULL;
3926
      /* Look it up, if we have a cache */
3927
0
      if (self->dirmeta_cache)
3928
0
        cache_hit = g_hash_table_lookup (self->dirmeta_cache, sha256);
3929
0
      if (cache_hit)
3930
0
        *out_variant = g_variant_ref (cache_hit);
3931
0
      g_mutex_unlock (lock);
3932
0
      if (cache_hit)
3933
0
        return TRUE;
3934
0
    }
3935
3936
0
  _ostree_loose_path (loose_path_buf, sha256, objtype, self->mode);
3937
3938
0
  if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd, error))
3939
0
    return FALSE;
3940
3941
0
  if (fd < 0 && self->commit_stagedir.initialized)
3942
0
    {
3943
0
      if (!ot_openat_ignore_enoent (self->commit_stagedir.fd, loose_path_buf, &fd, error))
3944
0
        return FALSE;
3945
0
    }
3946
3947
0
  if (fd != -1)
3948
0
    {
3949
0
      struct stat stbuf;
3950
0
      if (!glnx_fstat (fd, &stbuf, error))
3951
0
        return FALSE;
3952
0
      if (out_variant)
3953
0
        {
3954
0
          if (!ot_variant_read_fd (fd, 0, ostree_metadata_variant_type (objtype), TRUE,
3955
0
                                   &ret_variant, error))
3956
0
            return FALSE;
3957
3958
          /* Now, let's put it in the cache */
3959
0
          if (is_dirmeta_cachable)
3960
0
            {
3961
0
              GMutex *lock = &self->cache_lock;
3962
0
              g_mutex_lock (lock);
3963
0
              if (self->dirmeta_cache)
3964
0
                g_hash_table_replace (self->dirmeta_cache, g_strdup (sha256),
3965
0
                                      g_variant_ref (ret_variant));
3966
0
              g_mutex_unlock (lock);
3967
0
            }
3968
0
        }
3969
0
      else if (out_stream)
3970
0
        {
3971
0
          ret_stream = g_unix_input_stream_new (fd, TRUE);
3972
0
          if (!ret_stream)
3973
0
            return FALSE;
3974
0
          fd = -1; /* Transfer ownership */
3975
0
        }
3976
3977
0
      if (out_size)
3978
0
        *out_size = stbuf.st_size;
3979
3980
0
      if (out_state)
3981
0
        {
3982
0
          g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (sha256);
3983
0
          *out_state = 0;
3984
3985
0
          glnx_autofd int commitpartial_fd = -1;
3986
0
          if (!ot_openat_ignore_enoent (self->repo_dir_fd, commitpartial_path, &commitpartial_fd,
3987
0
                                        error))
3988
0
            return FALSE;
3989
0
          if (commitpartial_fd != -1)
3990
0
            {
3991
0
              *out_state |= OSTREE_REPO_COMMIT_STATE_PARTIAL;
3992
0
              char reason;
3993
0
              if (read (commitpartial_fd, &reason, 1) == 1)
3994
0
                {
3995
0
                  if (reason == 'f')
3996
0
                    *out_state |= OSTREE_REPO_COMMIT_STATE_FSCK_PARTIAL;
3997
0
                }
3998
0
            }
3999
0
        }
4000
0
    }
4001
0
  else if (self->parent_repo)
4002
0
    {
4003
      /* Directly recurse to simplify out parameters */
4004
0
      return load_metadata_internal (self->parent_repo, objtype, sha256, error_if_not_found,
4005
0
                                     out_variant, out_stream, out_size, out_state, cancellable,
4006
0
                                     error);
4007
0
    }
4008
0
  else if (error_if_not_found)
4009
0
    {
4010
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No such metadata object %s.%s", sha256,
4011
0
                   ostree_object_type_to_string (objtype));
4012
0
      return FALSE;
4013
0
    }
4014
4015
0
  ot_transfer_out_value (out_variant, &ret_variant);
4016
0
  ot_transfer_out_value (out_stream, &ret_stream);
4017
0
  return TRUE;
4018
0
}
4019
4020
static GVariant *
4021
filemeta_to_stat (struct stat *stbuf, GVariant *metadata)
4022
0
{
4023
0
  guint32 uid, gid, mode;
4024
0
  GVariant *xattrs;
4025
4026
0
  g_variant_get (metadata, "(uuu@a(ayay))", &uid, &gid, &mode, &xattrs);
4027
0
  stbuf->st_uid = GUINT32_FROM_BE (uid);
4028
0
  stbuf->st_gid = GUINT32_FROM_BE (gid);
4029
0
  stbuf->st_mode = GUINT32_FROM_BE (mode);
4030
4031
0
  return xattrs;
4032
0
}
4033
4034
static gboolean
4035
repo_load_file_archive (OstreeRepo *self, const char *checksum, GInputStream **out_input,
4036
                        GFileInfo **out_file_info, GVariant **out_xattrs, GCancellable *cancellable,
4037
                        GError **error)
4038
0
{
4039
0
  struct stat stbuf;
4040
0
  char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
4041
0
  _ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
4042
4043
0
  glnx_autofd int fd = -1;
4044
0
  if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd, error))
4045
0
    return FALSE;
4046
4047
0
  if (fd < 0 && self->commit_stagedir.initialized)
4048
0
    {
4049
0
      if (!ot_openat_ignore_enoent (self->commit_stagedir.fd, loose_path_buf, &fd, error))
4050
0
        return FALSE;
4051
0
    }
4052
4053
0
  if (fd != -1)
4054
0
    {
4055
0
      if (!glnx_fstat (fd, &stbuf, error))
4056
0
        return FALSE;
4057
4058
0
      g_autoptr (GInputStream) tmp_stream = g_unix_input_stream_new (g_steal_fd (&fd), TRUE);
4059
      /* Note return here */
4060
0
      return ostree_content_stream_parse (TRUE, tmp_stream, stbuf.st_size, TRUE, out_input,
4061
0
                                          out_file_info, out_xattrs, cancellable, error);
4062
0
    }
4063
0
  else if (self->parent_repo)
4064
0
    {
4065
0
      return ostree_repo_load_file (self->parent_repo, checksum, out_input, out_file_info,
4066
0
                                    out_xattrs, cancellable, error);
4067
0
    }
4068
0
  else
4069
0
    {
4070
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Couldn't find file object '%s'",
4071
0
                   checksum);
4072
0
      return FALSE;
4073
0
    }
4074
0
}
4075
4076
static GVariant *
4077
_ostree_repo_read_xattrs_file_link (OstreeRepo *self, const char *checksum,
4078
                                    GCancellable *cancellable, GError **error)
4079
0
{
4080
0
  g_assert (self != NULL);
4081
0
  g_assert (checksum != NULL);
4082
4083
0
  char xattr_path[_OSTREE_LOOSE_PATH_MAX];
4084
0
  _ostree_loose_path (xattr_path, checksum, OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK, self->mode);
4085
4086
0
  g_autoptr (GVariant) xattrs = NULL;
4087
0
  glnx_autofd int fd = -1;
4088
0
  if (!glnx_openat_rdonly (self->objects_dir_fd, xattr_path, FALSE, &fd, error))
4089
0
    return FALSE;
4090
4091
0
  g_assert (fd >= 0);
4092
0
  if (!ot_variant_read_fd (fd, 0, G_VARIANT_TYPE ("a(ayay)"), TRUE, &xattrs, error))
4093
0
    return glnx_prefix_error_null (error, "Deserializing xattrs content");
4094
4095
0
  g_assert (xattrs != NULL);
4096
0
  return g_steal_pointer (&xattrs);
4097
0
}
4098
4099
gboolean
4100
_ostree_repo_load_file_bare (OstreeRepo *self, const char *checksum, int *out_fd,
4101
                             struct stat *out_stbuf, char **out_symlink, GVariant **out_xattrs,
4102
                             GCancellable *cancellable, GError **error)
4103
0
{
4104
  /* The bottom case recursing on the parent repo */
4105
0
  if (self == NULL)
4106
0
    {
4107
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Couldn't find file object '%s'",
4108
0
                   checksum);
4109
0
      return FALSE;
4110
0
    }
4111
4112
0
  const char *errprefix = glnx_strjoina ("Opening content object ", checksum);
4113
0
  GLNX_AUTO_PREFIX_ERROR (errprefix, error);
4114
4115
0
  struct stat stbuf;
4116
0
  glnx_autofd int fd = -1;
4117
0
  g_autofree char *ret_symlink = NULL;
4118
0
  g_autoptr (GVariant) ret_xattrs = NULL;
4119
0
  char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
4120
0
  _ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
4121
4122
  /* Do a fstatat() and find the object directory that contains this object */
4123
0
  int objdir_fd = self->objects_dir_fd;
4124
0
  int res;
4125
0
  if ((res = TEMP_FAILURE_RETRY (fstatat (objdir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW)))
4126
0
          < 0
4127
0
      && errno == ENOENT && self->commit_stagedir.initialized)
4128
0
    {
4129
0
      objdir_fd = self->commit_stagedir.fd;
4130
0
      res = TEMP_FAILURE_RETRY (fstatat (objdir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW));
4131
0
    }
4132
0
  if (res < 0 && errno != ENOENT)
4133
0
    return glnx_throw_errno_prefix (error, "fstat");
4134
0
  else if (res < 0)
4135
0
    {
4136
0
      g_assert (errno == ENOENT);
4137
0
      return _ostree_repo_load_file_bare (self->parent_repo, checksum, out_fd, out_stbuf,
4138
0
                                          out_symlink, out_xattrs, cancellable, error);
4139
0
    }
4140
4141
0
  const gboolean need_open = (out_fd || (out_xattrs && self->mode == OSTREE_REPO_MODE_BARE)
4142
0
                              || self->mode == OSTREE_REPO_MODE_BARE_USER);
4143
  /* If it's a regular file and we're requested to return the fd, do it now. As
4144
   * a special case in bare-user, we always do an open, since the stat() metadata
4145
   * lives there.
4146
   */
4147
0
  if (need_open && S_ISREG (stbuf.st_mode))
4148
0
    {
4149
0
      fd = openat (objdir_fd, loose_path_buf, O_CLOEXEC | O_RDONLY);
4150
0
      if (fd < 0)
4151
0
        return glnx_throw_errno_prefix (error, "openat");
4152
0
    }
4153
4154
0
  if (!(S_ISREG (stbuf.st_mode) || S_ISLNK (stbuf.st_mode)))
4155
0
    return glnx_throw (error, "Not a regular file or symlink");
4156
4157
  /* In the non-bare-user case, gather symlink info if requested */
4158
0
  if (self->mode != OSTREE_REPO_MODE_BARE_USER && S_ISLNK (stbuf.st_mode) && out_symlink)
4159
0
    {
4160
0
      ret_symlink = glnx_readlinkat_malloc (objdir_fd, loose_path_buf, cancellable, error);
4161
0
      if (!ret_symlink)
4162
0
        return FALSE;
4163
0
    }
4164
4165
0
  if (self->mode == OSTREE_REPO_MODE_BARE_USER)
4166
0
    {
4167
0
      g_autoptr (GBytes) bytes = glnx_fgetxattr_bytes (fd, "user.ostreemeta", error);
4168
0
      if (bytes == NULL)
4169
0
        return FALSE;
4170
4171
0
      g_autoptr (GVariant) metadata = g_variant_ref_sink (
4172
0
          g_variant_new_from_bytes (OSTREE_FILEMETA_GVARIANT_FORMAT, bytes, FALSE));
4173
0
      g_autoptr (GVariant) read_xattrs = filemeta_to_stat (&stbuf, metadata);
4174
      // Old versions of ostree may have written these xattrs in non-canonical form.
4175
      // In order to aid comparisons, let's canonicalize here.
4176
0
      ret_xattrs = _ostree_canonicalize_xattrs (read_xattrs);
4177
0
      if (S_ISLNK (stbuf.st_mode))
4178
0
        {
4179
0
          if (out_symlink)
4180
0
            {
4181
0
              char targetbuf[PATH_MAX + 1];
4182
0
              gsize target_size;
4183
0
              g_autoptr (GInputStream) target_input = g_unix_input_stream_new (fd, FALSE);
4184
0
              if (!g_input_stream_read_all (target_input, targetbuf, sizeof (targetbuf),
4185
0
                                            &target_size, cancellable, error))
4186
0
                return FALSE;
4187
4188
0
              ret_symlink = g_strndup (targetbuf, target_size);
4189
0
            }
4190
          /* In the symlink case, we don't want to return the bare-user fd */
4191
0
          glnx_close_fd (&fd);
4192
0
        }
4193
0
    }
4194
0
  else if (self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
4195
0
    {
4196
      /* Canonical info is: uid/gid is 0 and no xattrs, which
4197
         might be wrong and thus not validate correctly, but
4198
         at least we report something consistent. */
4199
0
      stbuf.st_uid = stbuf.st_gid = 0;
4200
4201
0
      if (out_xattrs)
4202
0
        {
4203
0
          GVariantBuilder builder;
4204
0
          g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
4205
0
          ret_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder));
4206
0
        }
4207
0
    }
4208
0
  else if (self->mode == OSTREE_REPO_MODE_BARE)
4209
0
    {
4210
0
      if (S_ISREG (stbuf.st_mode) && out_xattrs)
4211
0
        {
4212
0
          if (self->disable_xattrs)
4213
0
            ret_xattrs
4214
0
                = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0));
4215
0
          else
4216
0
            {
4217
0
              ret_xattrs = ostree_fs_get_all_xattrs (fd, cancellable, error);
4218
0
              if (!ret_xattrs)
4219
0
                return FALSE;
4220
0
            }
4221
0
        }
4222
0
      else if (S_ISLNK (stbuf.st_mode) && out_xattrs)
4223
0
        {
4224
0
          if (self->disable_xattrs)
4225
0
            ret_xattrs
4226
0
                = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0));
4227
0
          else if (!glnx_dfd_name_get_all_xattrs (objdir_fd, loose_path_buf, &ret_xattrs,
4228
0
                                                  cancellable, error))
4229
0
            return FALSE;
4230
0
        }
4231
0
    }
4232
0
  else if (self->mode == OSTREE_REPO_MODE_BARE_SPLIT_XATTRS)
4233
0
    {
4234
0
      if (out_xattrs)
4235
0
        {
4236
0
          ret_xattrs = _ostree_repo_read_xattrs_file_link (self, checksum, cancellable, error);
4237
0
          if (ret_xattrs == NULL)
4238
0
            return FALSE;
4239
0
        }
4240
0
    }
4241
0
  else
4242
0
    {
4243
0
      g_assert_not_reached ();
4244
0
    }
4245
4246
0
  if (out_fd)
4247
0
    *out_fd = g_steal_fd (&fd);
4248
0
  if (out_stbuf)
4249
0
    *out_stbuf = stbuf;
4250
0
  ot_transfer_out_value (out_symlink, &ret_symlink);
4251
0
  ot_transfer_out_value (out_xattrs, &ret_xattrs);
4252
0
  return TRUE;
4253
0
}
4254
4255
/**
4256
 * ostree_repo_load_file:
4257
 * @self: Repo
4258
 * @checksum: ASCII SHA256 checksum
4259
 * @out_input: (out) (optional) (nullable): File content
4260
 * @out_file_info: (out) (optional) (nullable): File information
4261
 * @out_xattrs: (out) (optional) (nullable): Extended attributes
4262
 * @cancellable: Cancellable
4263
 * @error: Error
4264
 *
4265
 * Load content object, decomposing it into three parts: the actual
4266
 * content (for regular files), the metadata, and extended attributes.
4267
 */
4268
gboolean
4269
ostree_repo_load_file (OstreeRepo *self, const char *checksum, GInputStream **out_input,
4270
                       GFileInfo **out_file_info, GVariant **out_xattrs, GCancellable *cancellable,
4271
                       GError **error)
4272
0
{
4273
0
  if (self->mode == OSTREE_REPO_MODE_ARCHIVE)
4274
0
    return repo_load_file_archive (self, checksum, out_input, out_file_info, out_xattrs,
4275
0
                                   cancellable, error);
4276
0
  else
4277
0
    {
4278
0
      glnx_autofd int fd = -1;
4279
0
      struct stat stbuf;
4280
0
      g_autofree char *symlink_target = NULL;
4281
0
      g_autoptr (GVariant) ret_xattrs = NULL;
4282
0
      if (!_ostree_repo_load_file_bare (self, checksum, out_input ? &fd : NULL,
4283
0
                                        out_file_info ? &stbuf : NULL,
4284
0
                                        out_file_info ? &symlink_target : NULL,
4285
0
                                        out_xattrs ? &ret_xattrs : NULL, cancellable, error))
4286
0
        return FALSE;
4287
4288
      /* Convert fd → GInputStream and struct stat → GFileInfo */
4289
0
      if (out_input)
4290
0
        {
4291
0
          if (fd != -1)
4292
0
            *out_input = g_unix_input_stream_new (g_steal_fd (&fd), TRUE);
4293
0
          else
4294
0
            *out_input = NULL;
4295
0
        }
4296
0
      if (out_file_info)
4297
0
        {
4298
0
          *out_file_info = _ostree_stbuf_to_gfileinfo (&stbuf);
4299
0
          if (S_ISLNK (stbuf.st_mode))
4300
0
            g_file_info_set_symlink_target (*out_file_info, symlink_target);
4301
0
          else
4302
0
            g_assert (S_ISREG (stbuf.st_mode));
4303
0
        }
4304
4305
0
      ot_transfer_out_value (out_xattrs, &ret_xattrs);
4306
0
      return TRUE;
4307
0
    }
4308
0
}
4309
4310
/**
4311
 * ostree_repo_load_object_stream:
4312
 * @self: Repo
4313
 * @objtype: Object type
4314
 * @checksum: ASCII SHA256 checksum
4315
 * @out_input: (out): Stream for object
4316
 * @out_size: (out): Length of @out_input
4317
 * @cancellable: Cancellable
4318
 * @error: Error
4319
 *
4320
 * Load object as a stream; useful when copying objects between
4321
 * repositories.
4322
 */
4323
gboolean
4324
ostree_repo_load_object_stream (OstreeRepo *self, OstreeObjectType objtype, const char *checksum,
4325
                                GInputStream **out_input, guint64 *out_size,
4326
                                GCancellable *cancellable, GError **error)
4327
0
{
4328
0
  guint64 size;
4329
0
  g_autoptr (GInputStream) ret_input = NULL;
4330
4331
0
  if (OSTREE_OBJECT_TYPE_IS_META (objtype))
4332
0
    {
4333
0
      if (!load_metadata_internal (self, objtype, checksum, TRUE, NULL, &ret_input, &size, NULL,
4334
0
                                   cancellable, error))
4335
0
        return FALSE;
4336
0
    }
4337
0
  else
4338
0
    {
4339
0
      g_autoptr (GInputStream) input = NULL;
4340
0
      g_autoptr (GFileInfo) finfo = NULL;
4341
0
      g_autoptr (GVariant) xattrs = NULL;
4342
4343
0
      if (!ostree_repo_load_file (self, checksum, &input, &finfo, &xattrs, cancellable, error))
4344
0
        return FALSE;
4345
4346
0
      if (!ostree_raw_file_to_content_stream (input, finfo, xattrs, &ret_input, &size, cancellable,
4347
0
                                              error))
4348
0
        return FALSE;
4349
0
    }
4350
4351
0
  ot_transfer_out_value (out_input, &ret_input);
4352
0
  *out_size = size;
4353
0
  return TRUE;
4354
0
}
4355
4356
/*
4357
 * _ostree_repo_has_loose_object:
4358
 * @loose_path_buf: Buffer of size _OSTREE_LOOSE_PATH_MAX
4359
 *
4360
 * Locate object in repository; if it exists, @out_is_stored will be
4361
 * set to TRUE.  @loose_path_buf is always set to the loose path.
4362
 */
4363
gboolean
4364
_ostree_repo_has_loose_object (OstreeRepo *self, const char *checksum, OstreeObjectType objtype,
4365
                               gboolean *out_is_stored, GCancellable *cancellable, GError **error)
4366
0
{
4367
0
  char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
4368
0
  _ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
4369
4370
0
  gboolean found = FALSE;
4371
  /* It's easier to share code if we make this an array */
4372
0
  int dfd_searches[] = { -1, self->objects_dir_fd };
4373
0
  if (self->commit_stagedir.initialized)
4374
0
    dfd_searches[0] = self->commit_stagedir.fd;
4375
0
  for (guint i = 0; i < G_N_ELEMENTS (dfd_searches); i++)
4376
0
    {
4377
0
      int dfd = dfd_searches[i];
4378
0
      if (dfd == -1)
4379
0
        continue;
4380
0
      struct stat stbuf;
4381
0
      if (TEMP_FAILURE_RETRY (fstatat (dfd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW)) < 0)
4382
0
        {
4383
0
          if (errno == ENOENT)
4384
0
            ; /* Next dfd */
4385
0
          else
4386
0
            return glnx_throw_errno_prefix (error, "fstatat(%s)", loose_path_buf);
4387
0
        }
4388
0
      else
4389
0
        {
4390
0
          found = TRUE;
4391
0
          break;
4392
0
        }
4393
0
    }
4394
4395
0
  *out_is_stored = found;
4396
0
  return TRUE;
4397
0
}
4398
4399
/**
4400
 * ostree_repo_has_object:
4401
 * @self: Repo
4402
 * @objtype: Object type
4403
 * @checksum: ASCII SHA256 checksum
4404
 * @out_have_object: (out): %TRUE if repository contains object
4405
 * @cancellable: Cancellable
4406
 * @error: Error
4407
 *
4408
 * Set @out_have_object to %TRUE if @self contains the given object;
4409
 * %FALSE otherwise.
4410
 *
4411
 * Returns: %FALSE if an unexpected error occurred, %TRUE otherwise
4412
 */
4413
gboolean
4414
ostree_repo_has_object (OstreeRepo *self, OstreeObjectType objtype, const char *checksum,
4415
                        gboolean *out_have_object, GCancellable *cancellable, GError **error)
4416
0
{
4417
0
  gboolean ret_have_object = FALSE;
4418
4419
0
  if (!_ostree_repo_has_loose_object (self, checksum, objtype, &ret_have_object, cancellable,
4420
0
                                      error))
4421
0
    return FALSE;
4422
4423
  /* In the future, here is where we would also look up in metadata pack files */
4424
4425
0
  if (!ret_have_object && self->parent_repo)
4426
0
    {
4427
0
      if (!ostree_repo_has_object (self->parent_repo, objtype, checksum, &ret_have_object,
4428
0
                                   cancellable, error))
4429
0
        return FALSE;
4430
0
    }
4431
4432
0
  if (out_have_object)
4433
0
    *out_have_object = ret_have_object;
4434
0
  return TRUE;
4435
0
}
4436
4437
/**
4438
 * ostree_repo_delete_object:
4439
 * @self: Repo
4440
 * @objtype: Object type
4441
 * @sha256: Checksum
4442
 * @cancellable: Cancellable
4443
 * @error: Error
4444
 *
4445
 * Remove the object of type @objtype with checksum @sha256
4446
 * from the repository.  An error of type %G_IO_ERROR_NOT_FOUND
4447
 * is thrown if the object does not exist.
4448
 */
4449
gboolean
4450
ostree_repo_delete_object (OstreeRepo *self, OstreeObjectType objtype, const char *sha256,
4451
                           GCancellable *cancellable, GError **error)
4452
0
{
4453
0
  char loose_path[_OSTREE_LOOSE_PATH_MAX];
4454
0
  _ostree_loose_path (loose_path, sha256, objtype, self->mode);
4455
4456
0
  if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
4457
0
    {
4458
0
      char meta_loose[_OSTREE_LOOSE_PATH_MAX];
4459
4460
0
      _ostree_loose_path (meta_loose, sha256, OSTREE_OBJECT_TYPE_COMMIT_META, self->mode);
4461
4462
0
      if (!ot_ensure_unlinked_at (self->objects_dir_fd, meta_loose, error))
4463
0
        return FALSE;
4464
0
    }
4465
4466
0
  if (!glnx_unlinkat (self->objects_dir_fd, loose_path, 0, error))
4467
0
    return glnx_prefix_error (error, "Deleting object %s.%s", sha256,
4468
0
                              ostree_object_type_to_string (objtype));
4469
4470
  /* If the repository is configured to use tombstone commits, create one when deleting a commit.
4471
   */
4472
0
  if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
4473
0
    {
4474
0
      gboolean tombstone_commits = FALSE;
4475
0
      GKeyFile *readonly_config = ostree_repo_get_config (self);
4476
0
      if (!ot_keyfile_get_boolean_with_default (readonly_config, "core", "tombstone-commits", FALSE,
4477
0
                                                &tombstone_commits, error))
4478
0
        return FALSE;
4479
4480
0
      if (tombstone_commits)
4481
0
        {
4482
0
          g_auto (GVariantBuilder) builder = OT_VARIANT_BUILDER_INITIALIZER;
4483
0
          g_autoptr (GVariant) variant = NULL;
4484
4485
0
          g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
4486
0
          g_variant_builder_add (&builder, "{sv}", "commit", g_variant_new_bytestring (sha256));
4487
0
          variant = g_variant_ref_sink (g_variant_builder_end (&builder));
4488
0
          if (!ostree_repo_write_metadata_trusted (self, OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT,
4489
0
                                                   sha256, variant, cancellable, error))
4490
0
            return FALSE;
4491
0
        }
4492
0
    }
4493
4494
0
  return TRUE;
4495
0
}
4496
4497
/* Thin wrapper for _ostree_verify_metadata_object() */
4498
static gboolean
4499
fsck_metadata_object (OstreeRepo *self, OstreeObjectType objtype, const char *sha256,
4500
                      GCancellable *cancellable, GError **error)
4501
0
{
4502
0
  const char *errmsg = glnx_strjoina ("fsck ", sha256, ".", ostree_object_type_to_string (objtype));
4503
0
  GLNX_AUTO_PREFIX_ERROR (errmsg, error);
4504
0
  g_autoptr (GVariant) metadata = NULL;
4505
0
  if (!load_metadata_internal (self, objtype, sha256, TRUE, &metadata, NULL, NULL, NULL,
4506
0
                               cancellable, error))
4507
0
    return FALSE;
4508
4509
0
  return _ostree_verify_metadata_object (objtype, sha256, metadata, error);
4510
0
}
4511
4512
static gboolean
4513
fsck_content_object (OstreeRepo *self, const char *sha256, GCancellable *cancellable,
4514
                     GError **error)
4515
0
{
4516
0
  const char *errmsg = glnx_strjoina ("fsck content object ", sha256);
4517
0
  GLNX_AUTO_PREFIX_ERROR (errmsg, error);
4518
0
  g_autoptr (GInputStream) input = NULL;
4519
0
  g_autoptr (GFileInfo) file_info = NULL;
4520
0
  g_autoptr (GVariant) xattrs = NULL;
4521
4522
0
  if (!ostree_repo_load_file (self, sha256, &input, &file_info, &xattrs, cancellable, error))
4523
0
    return FALSE;
4524
4525
  /* TODO more consistency checks here */
4526
0
  const guint32 mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
4527
0
  if (!ostree_validate_structureof_file_mode (mode, error))
4528
0
    return FALSE;
4529
4530
0
  g_autofree guchar *computed_csum = NULL;
4531
0
  if (!ostree_checksum_file_from_input (file_info, xattrs, input, OSTREE_OBJECT_TYPE_FILE,
4532
0
                                        &computed_csum, cancellable, error))
4533
0
    return FALSE;
4534
4535
0
  char actual_checksum[OSTREE_SHA256_STRING_LEN + 1];
4536
0
  ostree_checksum_inplace_from_bytes (computed_csum, actual_checksum);
4537
0
  return _ostree_compare_object_checksum (OSTREE_OBJECT_TYPE_FILE, sha256, actual_checksum, error);
4538
0
}
4539
4540
/**
4541
 * ostree_repo_fsck_object:
4542
 * @self: Repo
4543
 * @objtype: Object type
4544
 * @sha256: Checksum
4545
 * @cancellable: Cancellable
4546
 * @error: Error
4547
 *
4548
 * Verify consistency of the object; this performs checks only relevant to the
4549
 * immediate object itself, such as checksumming. This API call will not itself
4550
 * traverse metadata objects for example.
4551
 *
4552
 * Since: 2017.15
4553
 */
4554
gboolean
4555
ostree_repo_fsck_object (OstreeRepo *self, OstreeObjectType objtype, const char *sha256,
4556
                         GCancellable *cancellable, GError **error)
4557
0
{
4558
0
  if (OSTREE_OBJECT_TYPE_IS_META (objtype))
4559
0
    return fsck_metadata_object (self, objtype, sha256, cancellable, error);
4560
0
  else
4561
0
    return fsck_content_object (self, sha256, cancellable, error);
4562
0
}
4563
4564
/**
4565
 * ostree_repo_import_object_from:
4566
 * @self: Destination repo
4567
 * @source: Source repo
4568
 * @objtype: Object type
4569
 * @checksum: checksum
4570
 * @cancellable: Cancellable
4571
 * @error: Error
4572
 *
4573
 * Copy object named by @objtype and @checksum into @self from the
4574
 * source repository @source.  If both repositories are of the same
4575
 * type and on the same filesystem, this will simply be a fast Unix
4576
 * hard link operation.
4577
 *
4578
 * Otherwise, a copy will be performed.
4579
 */
4580
gboolean
4581
ostree_repo_import_object_from (OstreeRepo *self, OstreeRepo *source, OstreeObjectType objtype,
4582
                                const char *checksum, GCancellable *cancellable, GError **error)
4583
0
{
4584
0
  return ostree_repo_import_object_from_with_trust (self, source, objtype, checksum, TRUE,
4585
0
                                                    cancellable, error);
4586
0
}
4587
4588
/**
4589
 * ostree_repo_import_object_from_with_trust:
4590
 * @self: Destination repo
4591
 * @source: Source repo
4592
 * @objtype: Object type
4593
 * @checksum: checksum
4594
 * @trusted: If %TRUE, assume the source repo is valid and trusted
4595
 * @cancellable: Cancellable
4596
 * @error: Error
4597
 *
4598
 * Copy object named by @objtype and @checksum into @self from the
4599
 * source repository @source. If @trusted is %TRUE and both
4600
 * repositories are of the same type and on the same filesystem,
4601
 * this will simply be a fast Unix hard link operation.
4602
 *
4603
 * Otherwise, a copy will be performed.
4604
 *
4605
 * Since: 2016.5
4606
 */
4607
gboolean
4608
ostree_repo_import_object_from_with_trust (OstreeRepo *self, OstreeRepo *source,
4609
                                           OstreeObjectType objtype, const char *checksum,
4610
                                           gboolean trusted, GCancellable *cancellable,
4611
                                           GError **error)
4612
0
{
4613
  /* This just wraps a currently internal API, may make it public later */
4614
0
  OstreeRepoImportFlags flags = trusted ? _OSTREE_REPO_IMPORT_FLAGS_TRUSTED : 0;
4615
0
  return _ostree_repo_import_object (self, source, objtype, checksum, flags, cancellable, error);
4616
0
}
4617
4618
/**
4619
 * ostree_repo_query_object_storage_size:
4620
 * @self: Repo
4621
 * @objtype: Object type
4622
 * @sha256: Checksum
4623
 * @out_size: (out): Size in bytes object occupies physically
4624
 * @cancellable: Cancellable
4625
 * @error: Error
4626
 *
4627
 * Return the size in bytes of object with checksum @sha256, after any
4628
 * compression has been applied.
4629
 */
4630
gboolean
4631
ostree_repo_query_object_storage_size (OstreeRepo *self, OstreeObjectType objtype,
4632
                                       const char *sha256, guint64 *out_size,
4633
                                       GCancellable *cancellable, GError **error)
4634
0
{
4635
0
  char loose_path[_OSTREE_LOOSE_PATH_MAX];
4636
0
  _ostree_loose_path (loose_path, sha256, objtype, self->mode);
4637
0
  int res;
4638
4639
0
  struct stat stbuf;
4640
0
  res = TEMP_FAILURE_RETRY (
4641
0
      fstatat (self->objects_dir_fd, loose_path, &stbuf, AT_SYMLINK_NOFOLLOW));
4642
0
  if (res < 0 && errno == ENOENT && self->commit_stagedir.initialized)
4643
0
    res = TEMP_FAILURE_RETRY (
4644
0
        fstatat (self->commit_stagedir.fd, loose_path, &stbuf, AT_SYMLINK_NOFOLLOW));
4645
4646
0
  if (res < 0)
4647
0
    return glnx_throw_errno_prefix (error, "Querying object %s.%s", sha256,
4648
0
                                    ostree_object_type_to_string (objtype));
4649
4650
0
  *out_size = stbuf.st_size;
4651
0
  return TRUE;
4652
0
}
4653
4654
/**
4655
 * ostree_repo_load_variant_if_exists:
4656
 * @self: Repo
4657
 * @objtype: Object type
4658
 * @sha256: ASCII checksum
4659
 * @out_variant: (out) (nullable) (transfer full): Metadata
4660
 * @error: Error
4661
 *
4662
 * Attempt to load the metadata object @sha256 of type @objtype if it
4663
 * exists, storing the result in @out_variant.  If it doesn't exist,
4664
 * @out_variant will be set to %NULL and the function will still
4665
 * return TRUE.
4666
 */
4667
gboolean
4668
ostree_repo_load_variant_if_exists (OstreeRepo *self, OstreeObjectType objtype, const char *sha256,
4669
                                    GVariant **out_variant, GError **error)
4670
0
{
4671
0
  return load_metadata_internal (self, objtype, sha256, FALSE, out_variant, NULL, NULL, NULL, NULL,
4672
0
                                 error);
4673
0
}
4674
4675
/**
4676
 * ostree_repo_load_variant:
4677
 * @self: Repo
4678
 * @objtype: Expected object type
4679
 * @sha256: Checksum string
4680
 * @out_variant: (out) (transfer full): Metadata object
4681
 * @error: Error
4682
 *
4683
 * Load the metadata object @sha256 of type @objtype, storing the
4684
 * result in @out_variant.
4685
 */
4686
gboolean
4687
ostree_repo_load_variant (OstreeRepo *self, OstreeObjectType objtype, const char *sha256,
4688
                          GVariant **out_variant, GError **error)
4689
0
{
4690
0
  return load_metadata_internal (self, objtype, sha256, TRUE, out_variant, NULL, NULL, NULL, NULL,
4691
0
                                 error);
4692
0
}
4693
4694
/**
4695
 * ostree_repo_load_commit:
4696
 * @self: Repo
4697
 * @checksum: Commit checksum
4698
 * @out_commit: (out) (allow-none): Commit
4699
 * @out_state: (out) (allow-none): Commit state
4700
 * @error: Error
4701
 *
4702
 * A version of ostree_repo_load_variant() specialized to commits,
4703
 * capable of returning extended state information.  Currently
4704
 * the only extended state is %OSTREE_REPO_COMMIT_STATE_PARTIAL, which
4705
 * means that only a sub-path of the commit is available.
4706
 */
4707
gboolean
4708
ostree_repo_load_commit (OstreeRepo *self, const char *checksum, GVariant **out_variant,
4709
                         OstreeRepoCommitState *out_state, GError **error)
4710
0
{
4711
0
  return load_metadata_internal (self, OSTREE_OBJECT_TYPE_COMMIT, checksum, TRUE, out_variant, NULL,
4712
0
                                 NULL, out_state, NULL, error);
4713
0
}
4714
4715
static GHashTable *
4716
repo_list_objects_impl (OstreeRepo *self, OstreeRepoListObjectsFlags flags, GVariant *dummy_value,
4717
                        GCancellable *cancellable, GError **error)
4718
0
{
4719
0
  g_assert (error == NULL || *error == NULL);
4720
0
  g_assert (self->inited);
4721
4722
0
  g_autoptr (GHashTable) ret_objects = g_hash_table_new_full (
4723
0
      ostree_hash_object_name, g_variant_equal, (GDestroyNotify)g_variant_unref,
4724
0
      dummy_value ? (GDestroyNotify)g_variant_unref : NULL);
4725
4726
0
  if (flags & OSTREE_REPO_LIST_OBJECTS_ALL)
4727
0
    flags |= (OSTREE_REPO_LIST_OBJECTS_LOOSE | OSTREE_REPO_LIST_OBJECTS_PACKED);
4728
4729
0
  if (flags & OSTREE_REPO_LIST_OBJECTS_LOOSE)
4730
0
    {
4731
0
      if (!list_loose_objects (self, dummy_value, ret_objects, NULL, cancellable, error))
4732
0
        return FALSE;
4733
0
      if ((flags & OSTREE_REPO_LIST_OBJECTS_NO_PARENTS) == 0 && self->parent_repo)
4734
0
        {
4735
0
          if (!list_loose_objects (self->parent_repo, dummy_value, ret_objects, NULL, cancellable,
4736
0
                                   error))
4737
0
            return FALSE;
4738
0
        }
4739
0
    }
4740
4741
0
  if (flags & OSTREE_REPO_LIST_OBJECTS_PACKED)
4742
0
    {
4743
      /* Nothing for now... */
4744
0
    }
4745
4746
0
  return g_steal_pointer (&ret_objects);
4747
0
}
4748
4749
/* A currently-internal version of ostree_repo_list_objects which returns
4750
 * a set, and not a map (with a useless value).
4751
 */
4752
GHashTable *
4753
ostree_repo_list_objects_set (OstreeRepo *self, OstreeRepoListObjectsFlags flags,
4754
                              GCancellable *cancellable, GError **error)
4755
0
{
4756
0
  return repo_list_objects_impl (self, flags, NULL, cancellable, error);
4757
0
}
4758
4759
/* For unfortunate historical reasons we emit this dummy value.
4760
 * It was intended to provide additional information about the object (e.g. "is in a pack file")
4761
 * but we ended up not shipping pack files.
4762
 */
4763
static GVariant *
4764
get_dummy_list_objects_variant (void)
4765
0
{
4766
0
  return g_variant_ref_sink (g_variant_new ("(b@as)", TRUE, g_variant_new_strv (NULL, 0)));
4767
0
}
4768
4769
/**
4770
 * ostree_repo_list_objects:
4771
 * @self: Repo
4772
 * @flags: Flags controlling enumeration
4773
 * @out_objects: (out) (transfer container) (element-type GVariant GVariant):
4774
 * Map of serialized object name to variant data
4775
 * @cancellable: Cancellable
4776
 * @error: Error
4777
 *
4778
 * This function synchronously enumerates all objects in the
4779
 * repository, returning data in @out_objects.  @out_objects
4780
 * maps from keys returned by ostree_object_name_serialize()
4781
 * to #GVariant values of type %OSTREE_REPO_LIST_OBJECTS_VARIANT_TYPE.
4782
 *
4783
 * Returns: %TRUE on success, %FALSE on error, and @error will be set
4784
 */
4785
gboolean
4786
ostree_repo_list_objects (OstreeRepo *self, OstreeRepoListObjectsFlags flags,
4787
                          GHashTable **out_objects, GCancellable *cancellable, GError **error)
4788
0
{
4789
0
  g_autoptr (GVariant) dummy_value = get_dummy_list_objects_variant ();
4790
0
  g_autoptr (GHashTable) ret
4791
0
      = repo_list_objects_impl (self, flags, dummy_value, cancellable, error);
4792
0
  if (!ret)
4793
0
    return FALSE;
4794
0
  ot_transfer_out_value (out_objects, &ret);
4795
0
  return TRUE;
4796
0
}
4797
4798
/**
4799
 * ostree_repo_list_commit_objects_starting_with:
4800
 * @self: Repo
4801
 * @start: List commits starting with this checksum (empty string for all)
4802
 * @out_commits: (out) (transfer container) (element-type GVariant GVariant):
4803
 * Map of serialized commit name to variant data
4804
 * @cancellable: Cancellable
4805
 * @error: Error
4806
 *
4807
 * This function synchronously enumerates all commit objects starting
4808
 * with @start, returning data in @out_commits.
4809
 *
4810
 * To list all commit objects, provide the empty string `""` for @start.
4811
 *
4812
 * Returns: %TRUE on success, %FALSE on error, and @error will be set
4813
 */
4814
gboolean
4815
ostree_repo_list_commit_objects_starting_with (OstreeRepo *self, const char *start,
4816
                                               GHashTable **out_commits, GCancellable *cancellable,
4817
                                               GError **error)
4818
0
{
4819
0
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4820
0
  g_return_val_if_fail (self->inited, FALSE);
4821
4822
0
  g_autoptr (GHashTable) ret_commits
4823
0
      = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
4824
0
                               (GDestroyNotify)g_variant_unref, (GDestroyNotify)g_variant_unref);
4825
0
  g_autoptr (GVariant) dummy_loose_object_variant = get_dummy_list_objects_variant ();
4826
4827
0
  if (!list_loose_objects (self, dummy_loose_object_variant, ret_commits, start, cancellable,
4828
0
                           error))
4829
0
    return FALSE;
4830
4831
0
  if (self->parent_repo)
4832
0
    {
4833
0
      if (!list_loose_objects (self->parent_repo, dummy_loose_object_variant, ret_commits, start,
4834
0
                               cancellable, error))
4835
0
        return FALSE;
4836
0
    }
4837
4838
0
  ot_transfer_out_value (out_commits, &ret_commits);
4839
0
  return TRUE;
4840
0
}
4841
4842
/**
4843
 * ostree_repo_read_commit:
4844
 * @self: Repo
4845
 * @ref: Ref or ASCII checksum
4846
 * @out_root: (out) (optional): An #OstreeRepoFile corresponding to the root
4847
 * @out_commit: (out) (optional): The resolved commit checksum
4848
 * @cancellable: Cancellable
4849
 * @error: Error
4850
 *
4851
 * Load the content for @rev into @out_root.
4852
 */
4853
gboolean
4854
ostree_repo_read_commit (OstreeRepo *self, const char *ref, GFile **out_root, char **out_commit,
4855
                         GCancellable *cancellable, GError **error)
4856
0
{
4857
0
  g_autofree char *resolved_commit = NULL;
4858
0
  if (!ostree_repo_resolve_rev (self, ref, FALSE, &resolved_commit, error))
4859
0
    return FALSE;
4860
4861
0
  g_autoptr (GFile) ret_root
4862
0
      = (GFile *)_ostree_repo_file_new_for_commit (self, resolved_commit, error);
4863
0
  if (!ret_root)
4864
0
    return FALSE;
4865
4866
0
  if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile *)ret_root, error))
4867
0
    return FALSE;
4868
4869
0
  ot_transfer_out_value (out_root, &ret_root);
4870
0
  ot_transfer_out_value (out_commit, &resolved_commit);
4871
0
  return TRUE;
4872
0
}
4873
4874
/**
4875
 * ostree_repo_pull:
4876
 * @self: Repo
4877
 * @remote_name: Name of remote
4878
 * @refs_to_fetch: (array zero-terminated=1) (element-type utf8) (allow-none): Optional list of
4879
 * refs; if %NULL, fetch all configured refs
4880
 * @flags: Options controlling fetch behavior
4881
 * @progress: (allow-none): Progress
4882
 * @cancellable: Cancellable
4883
 * @error: Error
4884
 *
4885
 * Connect to the remote repository, fetching the specified set of
4886
 * refs @refs_to_fetch.  For each ref that is changed, download the
4887
 * commit, all metadata, and all content objects, storing them safely
4888
 * on disk in @self.
4889
 *
4890
 * If @flags contains %OSTREE_REPO_PULL_FLAGS_MIRROR, and
4891
 * the @refs_to_fetch is %NULL, and the remote repository contains a
4892
 * summary file, then all refs will be fetched.
4893
 *
4894
 * If @flags contains %OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY, then only the
4895
 * metadata for the commits in @refs_to_fetch is pulled.
4896
 *
4897
 * Warning: This API will iterate the thread default main context,
4898
 * which is a bug, but kept for compatibility reasons.  If you want to
4899
 * avoid this, use g_main_context_push_thread_default() to push a new
4900
 * one around this call.
4901
 */
4902
gboolean
4903
ostree_repo_pull (OstreeRepo *self, const char *remote_name, char **refs_to_fetch,
4904
                  OstreeRepoPullFlags flags, OstreeAsyncProgress *progress,
4905
                  GCancellable *cancellable, GError **error)
4906
0
{
4907
0
  return ostree_repo_pull_one_dir (self, remote_name, NULL, refs_to_fetch, flags, progress,
4908
0
                                   cancellable, error);
4909
0
}
4910
4911
/**
4912
 * ostree_repo_pull_one_dir:
4913
 * @self: Repo
4914
 * @remote_name: Name of remote
4915
 * @dir_to_pull: Subdirectory path
4916
 * @refs_to_fetch: (array zero-terminated=1) (element-type utf8) (allow-none): Optional list of
4917
 * refs; if %NULL, fetch all configured refs
4918
 * @flags: Options controlling fetch behavior
4919
 * @progress: (allow-none): Progress
4920
 * @cancellable: Cancellable
4921
 * @error: Error
4922
 *
4923
 * This is similar to ostree_repo_pull(), but only fetches a single
4924
 * subpath.
4925
 */
4926
gboolean
4927
ostree_repo_pull_one_dir (OstreeRepo *self, const char *remote_name, const char *dir_to_pull,
4928
                          char **refs_to_fetch, OstreeRepoPullFlags flags,
4929
                          OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error)
4930
0
{
4931
0
  GVariantBuilder builder;
4932
0
  g_autoptr (GVariant) options = NULL;
4933
4934
0
  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
4935
4936
0
  if (dir_to_pull)
4937
0
    g_variant_builder_add (&builder, "{s@v}", "subdir",
4938
0
                           g_variant_new_variant (g_variant_new_string (dir_to_pull)));
4939
0
  g_variant_builder_add (&builder, "{s@v}", "flags",
4940
0
                         g_variant_new_variant (g_variant_new_int32 (flags)));
4941
0
  if (refs_to_fetch)
4942
0
    g_variant_builder_add (
4943
0
        &builder, "{s@v}", "refs",
4944
0
        g_variant_new_variant (g_variant_new_strv ((const char *const *)refs_to_fetch, -1)));
4945
4946
0
  options = g_variant_ref_sink (g_variant_builder_end (&builder));
4947
0
  return ostree_repo_pull_with_options (self, remote_name, options, progress, cancellable, error);
4948
0
}
4949
4950
/**
4951
 * _formatted_time_remaining_from_seconds:
4952
 * @seconds_remaining: Estimated number of seconds remaining.
4953
 *
4954
 * Returns a strings showing the number of days, hours, minutes
4955
 * and seconds remaining.
4956
 **/
4957
static char *
4958
_formatted_time_remaining_from_seconds (guint64 seconds_remaining)
4959
0
{
4960
0
  guint64 minutes_remaining = seconds_remaining / 60;
4961
0
  guint64 hours_remaining = minutes_remaining / 60;
4962
0
  guint64 days_remaining = hours_remaining / 24;
4963
4964
0
  GString *description = g_string_new (NULL);
4965
4966
0
  if (days_remaining)
4967
0
    g_string_append_printf (description, "%" G_GUINT64_FORMAT " days ", days_remaining);
4968
4969
0
  if (hours_remaining)
4970
0
    g_string_append_printf (description, "%" G_GUINT64_FORMAT " hours ", hours_remaining % 24);
4971
4972
0
  if (minutes_remaining)
4973
0
    g_string_append_printf (description, "%" G_GUINT64_FORMAT " minutes ", minutes_remaining % 60);
4974
4975
0
  g_string_append_printf (description, "%" G_GUINT64_FORMAT " seconds ", seconds_remaining % 60);
4976
4977
0
  return g_string_free (description, FALSE);
4978
0
}
4979
4980
/**
4981
 * ostree_repo_pull_default_console_progress_changed:
4982
 * @progress: Async progress
4983
 * @user_data: (allow-none): User data
4984
 *
4985
 * Convenient "changed" callback for use with
4986
 * ostree_async_progress_new_and_connect() when pulling from a remote
4987
 * repository.
4988
 *
4989
 * Depending on the state of the #OstreeAsyncProgress, either displays a
4990
 * custom status message, or else outstanding fetch progress in bytes/sec,
4991
 * or else outstanding content or metadata writes to the repository in
4992
 * number of objects.
4993
 *
4994
 * Compatibility note: this function previously assumed that @user_data
4995
 * was a pointer to a #GSConsole instance.  This is no longer the case,
4996
 * and @user_data is ignored.
4997
 **/
4998
void
4999
ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress,
5000
                                                   gpointer user_data)
5001
0
{
5002
0
  g_autofree char *status = NULL;
5003
0
  gboolean caught_error, scanning;
5004
0
  guint outstanding_fetches;
5005
0
  guint outstanding_metadata_fetches;
5006
0
  guint outstanding_writes;
5007
0
  guint n_scanned_metadata;
5008
0
  guint fetched_delta_parts;
5009
0
  guint total_delta_parts;
5010
0
  guint fetched_delta_part_fallbacks;
5011
0
  guint total_delta_part_fallbacks;
5012
5013
0
  g_autoptr (GString) buf = g_string_new ("");
5014
5015
0
  ostree_async_progress_get (
5016
0
      progress, "outstanding-fetches", "u", &outstanding_fetches, "outstanding-metadata-fetches",
5017
0
      "u", &outstanding_metadata_fetches, "outstanding-writes", "u", &outstanding_writes,
5018
0
      "caught-error", "b", &caught_error, "scanning", "u", &scanning, "scanned-metadata", "u",
5019
0
      &n_scanned_metadata, "fetched-delta-parts", "u", &fetched_delta_parts, "total-delta-parts",
5020
0
      "u", &total_delta_parts, "fetched-delta-fallbacks", "u", &fetched_delta_part_fallbacks,
5021
0
      "total-delta-fallbacks", "u", &total_delta_part_fallbacks, "status", "s", &status, NULL);
5022
5023
0
  if (*status != '\0')
5024
0
    {
5025
0
      g_string_append (buf, status);
5026
0
    }
5027
0
  else if (caught_error)
5028
0
    {
5029
0
      g_string_append_printf (buf, "Caught error, waiting for outstanding tasks");
5030
0
    }
5031
0
  else if (outstanding_fetches)
5032
0
    {
5033
0
      guint64 bytes_transferred, start_time, total_delta_part_size;
5034
0
      guint fetched, metadata_fetched, requested;
5035
0
      guint64 current_time = g_get_monotonic_time ();
5036
0
      g_autofree char *formatted_bytes_transferred = NULL;
5037
0
      g_autofree char *formatted_bytes_sec = NULL;
5038
0
      guint64 bytes_sec;
5039
5040
      /* Note: This is not atomic wrt the above getter call. */
5041
0
      ostree_async_progress_get (progress, "bytes-transferred", "t", &bytes_transferred, "fetched",
5042
0
                                 "u", &fetched, "metadata-fetched", "u", &metadata_fetched,
5043
0
                                 "requested", "u", &requested, "start-time", "t", &start_time,
5044
0
                                 "total-delta-part-size", "t", &total_delta_part_size, NULL);
5045
5046
0
      formatted_bytes_transferred = g_format_size_full (bytes_transferred, 0);
5047
5048
      /* Ignore the first second, or when we haven't transferred any
5049
       * data, since those could cause divide by zero below.
5050
       */
5051
0
      if ((current_time - start_time) < G_USEC_PER_SEC || bytes_transferred == 0)
5052
0
        {
5053
0
          bytes_sec = 0;
5054
0
          formatted_bytes_sec = g_strdup ("-");
5055
0
        }
5056
0
      else
5057
0
        {
5058
0
          bytes_sec = bytes_transferred / ((current_time - start_time) / G_USEC_PER_SEC);
5059
0
          formatted_bytes_sec = g_format_size (bytes_sec);
5060
0
        }
5061
5062
      /* Are we doing deltas?  If so, we can be more accurate */
5063
0
      if (total_delta_parts > 0)
5064
0
        {
5065
0
          guint64 fetched_delta_part_size
5066
0
              = ostree_async_progress_get_uint64 (progress, "fetched-delta-part-size");
5067
0
          g_autofree char *formatted_fetched = NULL;
5068
0
          g_autofree char *formatted_total = NULL;
5069
5070
          /* Here we merge together deltaparts + fallbacks to avoid bloating the text UI */
5071
0
          fetched_delta_parts += fetched_delta_part_fallbacks;
5072
0
          total_delta_parts += total_delta_part_fallbacks;
5073
5074
0
          formatted_fetched = g_format_size (fetched_delta_part_size);
5075
0
          formatted_total = g_format_size (total_delta_part_size);
5076
5077
0
          if (bytes_sec > 0)
5078
0
            {
5079
0
              guint64 est_time_remaining = 0;
5080
0
              if (total_delta_part_size > fetched_delta_part_size)
5081
0
                est_time_remaining = (total_delta_part_size - fetched_delta_part_size) / bytes_sec;
5082
0
              g_autofree char *formatted_est_time_remaining
5083
0
                  = _formatted_time_remaining_from_seconds (est_time_remaining);
5084
              /* No space between %s and remaining, since formatted_est_time_remaining has a
5085
               * trailing space */
5086
0
              g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/%s %s/s %sremaining",
5087
0
                                      fetched_delta_parts, total_delta_parts, formatted_fetched,
5088
0
                                      formatted_total, formatted_bytes_sec,
5089
0
                                      formatted_est_time_remaining);
5090
0
            }
5091
0
          else
5092
0
            {
5093
0
              g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/%s",
5094
0
                                      fetched_delta_parts, total_delta_parts, formatted_fetched,
5095
0
                                      formatted_total);
5096
0
            }
5097
0
        }
5098
0
      else if (scanning || outstanding_metadata_fetches)
5099
0
        {
5100
0
          g_string_append_printf (buf, "Receiving metadata objects: %u/(estimating) %s/s %s",
5101
0
                                  metadata_fetched, formatted_bytes_sec,
5102
0
                                  formatted_bytes_transferred);
5103
0
        }
5104
0
      else
5105
0
        {
5106
0
          g_string_append_printf (buf, "Receiving objects: %u%% (%u/%u) %s/s %s",
5107
0
                                  (guint)((((double)fetched) / requested) * 100), fetched,
5108
0
                                  requested, formatted_bytes_sec, formatted_bytes_transferred);
5109
0
        }
5110
0
    }
5111
0
  else if (outstanding_writes)
5112
0
    {
5113
0
      g_string_append_printf (buf, "Writing objects: %u", outstanding_writes);
5114
0
    }
5115
0
  else
5116
0
    {
5117
0
      g_string_append_printf (buf, "Scanning metadata: %u", n_scanned_metadata);
5118
0
    }
5119
5120
0
  glnx_console_text (buf->str);
5121
0
}
5122
5123
/**
5124
 * ostree_repo_append_gpg_signature:
5125
 * @self: Self
5126
 * @commit_checksum: SHA256 of given commit to sign
5127
 * @signature_bytes: Signature data
5128
 * @cancellable: A #GCancellable
5129
 * @error: a #GError
5130
 *
5131
 * Append a GPG signature to a commit.
5132
 */
5133
gboolean
5134
ostree_repo_append_gpg_signature (OstreeRepo *self, const gchar *commit_checksum,
5135
                                  GBytes *signature_bytes, GCancellable *cancellable,
5136
                                  GError **error)
5137
0
{
5138
0
  g_autoptr (GVariant) metadata = NULL;
5139
0
  if (!ostree_repo_read_commit_detached_metadata (self, commit_checksum, &metadata, cancellable,
5140
0
                                                  error))
5141
0
    return FALSE;
5142
5143
0
#ifndef OSTREE_DISABLE_GPGME
5144
0
  g_autoptr (GVariant) new_metadata
5145
0
      = _ostree_detached_metadata_append_gpg_sig (metadata, signature_bytes);
5146
5147
0
  if (!ostree_repo_write_commit_detached_metadata (self, commit_checksum, new_metadata, cancellable,
5148
0
                                                   error))
5149
0
    return FALSE;
5150
5151
0
  return TRUE;
5152
#else
5153
  return glnx_throw (error, "GPG feature is disabled in a build time");
5154
#endif /* OSTREE_DISABLE_GPGME */
5155
0
}
5156
5157
#ifndef OSTREE_DISABLE_GPGME
5158
static gboolean
5159
sign_data (OstreeRepo *self, GBytes *input_data, const gchar *key_id, const gchar *homedir,
5160
           GBytes **out_signature, GCancellable *cancellable, GError **error)
5161
0
{
5162
0
  g_auto (GLnxTmpfile) tmpf = {
5163
0
    0,
5164
0
  };
5165
0
  if (!glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_RDWR | O_CLOEXEC, &tmpf, error))
5166
0
    return FALSE;
5167
0
  g_autoptr (GOutputStream) tmp_signature_output = g_unix_output_stream_new (tmpf.fd, FALSE);
5168
5169
0
  g_auto (gpgme_ctx_t) context = ot_gpgme_new_ctx (homedir, error);
5170
0
  if (!context)
5171
0
    return FALSE;
5172
5173
  /* Get the secret keys with the given key id */
5174
0
  g_auto (gpgme_key_t) key = NULL;
5175
0
  gpgme_error_t err = gpgme_get_key (context, key_id, &key, 1);
5176
0
  if (gpgme_err_code (err) == GPG_ERR_EOF)
5177
0
    return glnx_throw (error, "No gpg key found with ID %s (homedir: %s)", key_id,
5178
0
                       homedir ? homedir : "<default>");
5179
0
  else if (gpgme_err_code (err) == GPG_ERR_AMBIGUOUS_NAME)
5180
0
    {
5181
0
      return glnx_throw (error,
5182
0
                         "gpg key id %s ambiguous (homedir: %s). Try the fingerprint instead",
5183
0
                         key_id, homedir ? homedir : "<default>");
5184
0
    }
5185
0
  else if (err != GPG_ERR_NO_ERROR)
5186
0
    return ot_gpgme_throw (err, error, "Unable to lookup key ID %s", key_id);
5187
5188
  /* Add the key to the context as a signer */
5189
0
  if ((err = gpgme_signers_add (context, key)) != GPG_ERR_NO_ERROR)
5190
0
    return ot_gpgme_throw (err, error, "Error signing commit");
5191
5192
  /* Get a gpg buffer from the commit */
5193
0
  g_auto (gpgme_data_t) commit_buffer = NULL;
5194
0
  gsize len;
5195
0
  const char *buf = g_bytes_get_data (input_data, &len);
5196
0
  if ((err = gpgme_data_new_from_mem (&commit_buffer, buf, len, FALSE)) != GPG_ERR_NO_ERROR)
5197
0
    return ot_gpgme_throw (err, error, "Failed to create buffer from commit file");
5198
5199
  /* Sign it */
5200
0
  g_auto (gpgme_data_t) signature_buffer = ot_gpgme_data_output (tmp_signature_output);
5201
0
  if ((err = gpgme_op_sign (context, commit_buffer, signature_buffer, GPGME_SIG_MODE_DETACH))
5202
0
      != GPG_ERR_NO_ERROR)
5203
0
    return ot_gpgme_throw (err, error, "Failure signing commit file");
5204
0
  if (!g_output_stream_close (tmp_signature_output, cancellable, error))
5205
0
    return FALSE;
5206
5207
  /* Return a mmap() reference */
5208
0
  g_autoptr (GMappedFile) signature_file = g_mapped_file_new_from_fd (tmpf.fd, FALSE, error);
5209
0
  if (!signature_file)
5210
0
    return FALSE;
5211
5212
0
  if (out_signature)
5213
0
    *out_signature = g_mapped_file_get_bytes (signature_file);
5214
0
  return TRUE;
5215
0
}
5216
#endif /* OSTREE_DISABLE_GPGME */
5217
5218
/**
5219
 * ostree_repo_sign_commit:
5220
 * @self: Self
5221
 * @commit_checksum: SHA256 of given commit to sign
5222
 * @key_id: Use this GPG key id
5223
 * @homedir: (allow-none): GPG home directory, or %NULL
5224
 * @cancellable: A #GCancellable
5225
 * @error: a #GError
5226
 *
5227
 * Add a GPG signature to a commit.
5228
 */
5229
gboolean
5230
ostree_repo_sign_commit (OstreeRepo *self, const gchar *commit_checksum, const gchar *key_id,
5231
                         const gchar *homedir, GCancellable *cancellable, GError **error)
5232
0
{
5233
0
#ifndef OSTREE_DISABLE_GPGME
5234
0
  g_autoptr (GBytes) commit_data = NULL;
5235
0
  g_autoptr (GBytes) signature = NULL;
5236
5237
0
  g_autoptr (GVariant) commit_variant = NULL;
5238
0
  if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum, &commit_variant,
5239
0
                                 error))
5240
0
    return glnx_prefix_error (error, "Failed to read commit");
5241
5242
0
  g_autoptr (GVariant) old_metadata = NULL;
5243
0
  if (!ostree_repo_read_commit_detached_metadata (self, commit_checksum, &old_metadata, cancellable,
5244
0
                                                  error))
5245
0
    return glnx_prefix_error (error, "Failed to read detached metadata");
5246
5247
0
  commit_data = g_variant_get_data_as_bytes (commit_variant);
5248
5249
  /* The verify operation is merely to parse any existing signatures to
5250
   * check if the commit has already been signed with the given key ID.
5251
   * We want to avoid storing duplicate signatures in the metadata. We
5252
   * pass the homedir so that the signing key can be imported, allowing
5253
   * subkey signatures to be recognised. */
5254
0
  g_autoptr (GError) local_error = NULL;
5255
0
  g_autoptr (GFile) verify_keydir = NULL;
5256
0
  if (homedir != NULL)
5257
0
    verify_keydir = g_file_new_for_path (homedir);
5258
0
  g_autoptr (OstreeGpgVerifyResult) result = _ostree_repo_gpg_verify_with_metadata (
5259
0
      self, commit_data, old_metadata, NULL, verify_keydir, NULL, cancellable, &local_error);
5260
0
  if (!result)
5261
0
    {
5262
      /* "Not found" just means the commit is not yet signed.  That's okay. */
5263
0
      if (g_error_matches (local_error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE))
5264
0
        {
5265
0
          g_clear_error (&local_error);
5266
0
        }
5267
0
      else
5268
0
        return g_propagate_error (error, g_steal_pointer (&local_error)), FALSE;
5269
0
    }
5270
0
  else if (ostree_gpg_verify_result_lookup (result, key_id, NULL))
5271
0
    {
5272
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, "Commit is already signed with GPG key %s",
5273
0
                   key_id);
5274
0
      return FALSE;
5275
0
    }
5276
5277
0
  if (!sign_data (self, commit_data, key_id, homedir, &signature, cancellable, error))
5278
0
    return FALSE;
5279
5280
0
  g_autoptr (GVariant) new_metadata
5281
0
      = _ostree_detached_metadata_append_gpg_sig (old_metadata, signature);
5282
5283
0
  if (!ostree_repo_write_commit_detached_metadata (self, commit_checksum, new_metadata, cancellable,
5284
0
                                                   error))
5285
0
    return FALSE;
5286
5287
0
  return TRUE;
5288
#else
5289
  /* FIXME: Return false until refactoring */
5290
  return glnx_throw (error, "GPG feature is disabled in a build time");
5291
#endif /* OSTREE_DISABLE_GPGME */
5292
0
}
5293
5294
/**
5295
 * ostree_repo_sign_delta:
5296
 * @self: Self
5297
 * @from_commit: From commit
5298
 * @to_commit: To commit
5299
 * @key_id: key id
5300
 * @homedir: homedir
5301
 * @cancellable: cancellable
5302
 * @error: error
5303
 *
5304
 * This function is deprecated, sign the summary file instead.
5305
 * Add a GPG signature to a static delta.
5306
 */
5307
gboolean
5308
ostree_repo_sign_delta (OstreeRepo *self, const gchar *from_commit, const gchar *to_commit,
5309
                        const gchar *key_id, const gchar *homedir, GCancellable *cancellable,
5310
                        GError **error)
5311
0
{
5312
0
  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "ostree_repo_sign_delta is deprecated");
5313
0
  return FALSE;
5314
0
}
5315
5316
static gboolean
5317
_ostree_repo_add_gpg_signature_summary_at (OstreeRepo *self, int dir_fd, const gchar **key_id,
5318
                                           const gchar *homedir, GCancellable *cancellable,
5319
                                           GError **error)
5320
0
{
5321
0
#ifndef OSTREE_DISABLE_GPGME
5322
0
  glnx_autofd int fd = -1;
5323
0
  if (!glnx_openat_rdonly (dir_fd, "summary", TRUE, &fd, error))
5324
0
    return FALSE;
5325
0
  g_autoptr (GBytes) summary_data = ot_fd_readall_or_mmap (fd, 0, error);
5326
0
  if (!summary_data)
5327
0
    return FALSE;
5328
  /* Note that fd is reused below */
5329
0
  glnx_close_fd (&fd);
5330
5331
0
  g_autoptr (GVariant) metadata = NULL;
5332
0
  if (!ot_openat_ignore_enoent (dir_fd, "summary.sig", &fd, error))
5333
0
    return FALSE;
5334
0
  if (fd >= 0)
5335
0
    {
5336
0
      if (!ot_variant_read_fd (fd, 0, G_VARIANT_TYPE (OSTREE_SUMMARY_SIG_GVARIANT_STRING), FALSE,
5337
0
                               &metadata, error))
5338
0
        return FALSE;
5339
0
    }
5340
5341
0
  for (guint i = 0; key_id[i]; i++)
5342
0
    {
5343
0
      g_autoptr (GBytes) signature_data = NULL;
5344
0
      if (!sign_data (self, summary_data, key_id[i], homedir, &signature_data, cancellable, error))
5345
0
        return FALSE;
5346
5347
0
      g_autoptr (GVariant) old_metadata = g_steal_pointer (&metadata);
5348
0
      metadata = _ostree_detached_metadata_append_gpg_sig (old_metadata, signature_data);
5349
0
    }
5350
5351
0
  g_autoptr (GVariant) normalized = g_variant_get_normal_form (metadata);
5352
5353
0
  if (!_ostree_repo_file_replace_contents (self, dir_fd, "summary.sig",
5354
0
                                           g_variant_get_data (normalized),
5355
0
                                           g_variant_get_size (normalized), cancellable, error))
5356
0
    return FALSE;
5357
5358
0
  return TRUE;
5359
#else
5360
  return glnx_throw (error, "GPG feature is disabled at build time");
5361
#endif /* OSTREE_DISABLE_GPGME */
5362
0
}
5363
5364
/**
5365
 * ostree_repo_add_gpg_signature_summary:
5366
 * @self: Self
5367
 * @key_id: (array zero-terminated=1) (element-type utf8): NULL-terminated array of GPG keys.
5368
 * @homedir: (allow-none): GPG home directory, or %NULL
5369
 * @cancellable: A #GCancellable
5370
 * @error: a #GError
5371
 *
5372
 * Add a GPG signature to a summary file.
5373
 */
5374
gboolean
5375
ostree_repo_add_gpg_signature_summary (OstreeRepo *self, const gchar **key_id, const gchar *homedir,
5376
                                       GCancellable *cancellable, GError **error)
5377
0
{
5378
0
#ifndef OSTREE_DISABLE_GPGME
5379
0
  return _ostree_repo_add_gpg_signature_summary_at (self, self->repo_dir_fd, key_id, homedir,
5380
0
                                                    cancellable, error);
5381
#else
5382
  return glnx_throw (error, "GPG feature is disabled in a build time");
5383
#endif /* OSTREE_DISABLE_GPGME */
5384
0
}
5385
5386
/**
5387
 * ostree_repo_gpg_sign_data:
5388
 * @self: Self
5389
 * @data: Data as a #GBytes
5390
 * @old_signatures: (nullable): Existing signatures to append to (or %NULL)
5391
 * @key_id: (array zero-terminated=1) (element-type utf8): NULL-terminated array of GPG keys.
5392
 * @homedir: (nullable): GPG home directory, or %NULL
5393
 * @out_signatures: (out): in case of success will contain signature
5394
 * @cancellable: A #GCancellable
5395
 * @error: a #GError
5396
 *
5397
 * Sign the given @data with the specified keys in @key_id. Similar to
5398
 * ostree_repo_add_gpg_signature_summary() but can be used on any
5399
 * data.
5400
 *
5401
 * You can use ostree_repo_gpg_verify_data() to verify the signatures.
5402
 *
5403
 * Returns: %TRUE if @data has been signed successfully,
5404
 * %FALSE in case of error (@error will contain the reason).
5405
 *
5406
 * Since: 2020.8
5407
 */
5408
gboolean
5409
ostree_repo_gpg_sign_data (OstreeRepo *self, GBytes *data, GBytes *old_signatures,
5410
                           const gchar **key_id, const gchar *homedir, GBytes **out_signatures,
5411
                           GCancellable *cancellable, GError **error)
5412
0
{
5413
0
#ifndef OSTREE_DISABLE_GPGME
5414
0
  g_autoptr (GVariant) metadata = NULL;
5415
0
  g_autoptr (GVariant) res = NULL;
5416
5417
0
  if (old_signatures)
5418
0
    metadata = g_variant_ref_sink (g_variant_new_from_bytes (
5419
0
        G_VARIANT_TYPE (OSTREE_SUMMARY_SIG_GVARIANT_STRING), old_signatures, FALSE));
5420
5421
0
  for (guint i = 0; key_id[i]; i++)
5422
0
    {
5423
0
      g_autoptr (GBytes) signature_data = NULL;
5424
0
      if (!sign_data (self, data, key_id[i], homedir, &signature_data, cancellable, error))
5425
0
        return FALSE;
5426
5427
0
      g_autoptr (GVariant) old_metadata = g_steal_pointer (&metadata);
5428
0
      metadata = _ostree_detached_metadata_append_gpg_sig (old_metadata, signature_data);
5429
0
    }
5430
5431
0
  res = g_variant_get_normal_form (metadata);
5432
0
  *out_signatures = g_variant_get_data_as_bytes (res);
5433
0
  return TRUE;
5434
#else
5435
  return glnx_throw (error, "GPG feature is disabled in a build time");
5436
#endif /* OSTREE_DISABLE_GPGME */
5437
0
}
5438
5439
#ifndef OSTREE_DISABLE_GPGME
5440
/* Special remote for _ostree_repo_gpg_verify_with_metadata() */
5441
static const char *OSTREE_ALL_REMOTES = "__OSTREE_ALL_REMOTES__";
5442
5443
/* Look for a keyring for @remote in the repo itself, or in
5444
 * /etc/ostree/remotes.d.
5445
 */
5446
static gboolean
5447
find_keyring (OstreeRepo *self, OstreeRemote *remote, GBytes **ret_bytes, GCancellable *cancellable,
5448
              GError **error)
5449
0
{
5450
0
  glnx_autofd int fd = -1;
5451
0
  if (!ot_openat_ignore_enoent (self->repo_dir_fd, remote->keyring, &fd, error))
5452
0
    return FALSE;
5453
5454
0
  if (fd != -1)
5455
0
    {
5456
0
      GBytes *ret = glnx_fd_readall_bytes (fd, cancellable, error);
5457
0
      if (!ret)
5458
0
        return FALSE;
5459
0
      *ret_bytes = ret;
5460
0
      return TRUE;
5461
0
    }
5462
5463
0
  g_autoptr (GFile) remotes_d = get_remotes_d_dir (self, NULL);
5464
0
  if (remotes_d)
5465
0
    {
5466
0
      g_autoptr (GFile) child = g_file_get_child (remotes_d, remote->keyring);
5467
5468
0
      if (!ot_openat_ignore_enoent (AT_FDCWD, gs_file_get_path_cached (child), &fd, error))
5469
0
        return FALSE;
5470
5471
0
      if (fd != -1)
5472
0
        {
5473
0
          GBytes *ret = glnx_fd_readall_bytes (fd, cancellable, error);
5474
0
          if (!ret)
5475
0
            return FALSE;
5476
0
          *ret_bytes = ret;
5477
0
          return TRUE;
5478
0
        }
5479
0
    }
5480
5481
0
  if (self->parent_repo)
5482
0
    return find_keyring (self->parent_repo, remote, ret_bytes, cancellable, error);
5483
5484
0
  *ret_bytes = NULL;
5485
0
  return TRUE;
5486
0
}
5487
5488
static gboolean
5489
_ostree_repo_gpg_prepare_verifier (OstreeRepo *self, const gchar *remote_name, GFile *keyringdir,
5490
                                   GFile *extra_keyring, gboolean add_global_keyrings,
5491
                                   OstreeGpgVerifier **out_verifier, GCancellable *cancellable,
5492
                                   GError **error)
5493
0
{
5494
0
  g_autoptr (OstreeGpgVerifier) verifier = _ostree_gpg_verifier_new ();
5495
5496
0
  if (remote_name == OSTREE_ALL_REMOTES)
5497
0
    {
5498
      /* Add all available remote keyring files. */
5499
5500
0
      if (!_ostree_gpg_verifier_add_keyring_dir_at (verifier, self->repo_dir_fd, ".", cancellable,
5501
0
                                                    error))
5502
0
        return FALSE;
5503
0
    }
5504
0
  else if (remote_name != NULL)
5505
0
    {
5506
      /* Add the remote's keyring file if it exists. */
5507
5508
0
      g_autoptr (OstreeRemote) remote = NULL;
5509
5510
0
      remote = _ostree_repo_get_remote_inherited (self, remote_name, error);
5511
0
      if (remote == NULL)
5512
0
        return FALSE;
5513
5514
0
      g_autoptr (GBytes) keyring_data = NULL;
5515
0
      if (!find_keyring (self, remote, &keyring_data, cancellable, error))
5516
0
        return FALSE;
5517
5518
0
      if (keyring_data != NULL)
5519
0
        {
5520
0
          _ostree_gpg_verifier_add_keyring_data (verifier, keyring_data, remote->keyring);
5521
0
          add_global_keyrings = FALSE;
5522
0
        }
5523
5524
0
      g_auto (GStrv) gpgkeypath_list = NULL;
5525
5526
0
      if (!ot_keyfile_get_string_list_with_separator_choice (
5527
0
              remote->options, remote->group, "gpgkeypath", ";,", &gpgkeypath_list, error))
5528
0
        return FALSE;
5529
5530
0
      if (gpgkeypath_list)
5531
0
        {
5532
0
          for (char **iter = gpgkeypath_list; *iter != NULL; ++iter)
5533
0
            if (!_ostree_gpg_verifier_add_keyfile_path (verifier, *iter, cancellable, error))
5534
0
              return FALSE;
5535
0
        }
5536
0
    }
5537
5538
0
  if (add_global_keyrings)
5539
0
    {
5540
      /* Use the deprecated global keyring directory. */
5541
0
      if (!_ostree_gpg_verifier_add_global_keyring_dir (verifier, cancellable, error))
5542
0
        return FALSE;
5543
0
    }
5544
5545
0
  if (keyringdir)
5546
0
    {
5547
0
      if (!_ostree_gpg_verifier_add_keyring_dir (verifier, keyringdir, cancellable, error))
5548
0
        return FALSE;
5549
0
    }
5550
0
  if (extra_keyring != NULL)
5551
0
    {
5552
0
      _ostree_gpg_verifier_add_keyring_file (verifier, extra_keyring);
5553
0
    }
5554
5555
0
  if (out_verifier != NULL)
5556
0
    *out_verifier = g_steal_pointer (&verifier);
5557
5558
0
  return TRUE;
5559
0
}
5560
5561
static OstreeGpgVerifyResult *
5562
_ostree_repo_gpg_verify_data_internal (OstreeRepo *self, const gchar *remote_name, GBytes *data,
5563
                                       GBytes *signatures, GFile *keyringdir, GFile *extra_keyring,
5564
                                       GCancellable *cancellable, GError **error)
5565
0
{
5566
0
  g_autoptr (OstreeGpgVerifier) verifier = NULL;
5567
0
  if (!_ostree_repo_gpg_prepare_verifier (self, remote_name, keyringdir, extra_keyring, TRUE,
5568
0
                                          &verifier, cancellable, error))
5569
0
    return NULL;
5570
5571
0
  return _ostree_gpg_verifier_check_signature (verifier, data, signatures, cancellable, error);
5572
0
}
5573
5574
OstreeGpgVerifyResult *
5575
_ostree_repo_gpg_verify_with_metadata (OstreeRepo *self, GBytes *signed_data, GVariant *metadata,
5576
                                       const char *remote_name, GFile *keyringdir,
5577
                                       GFile *extra_keyring, GCancellable *cancellable,
5578
                                       GError **error)
5579
0
{
5580
0
  g_autoptr (GVariant) signaturedata = NULL;
5581
0
  GByteArray *buffer;
5582
0
  GVariantIter iter;
5583
0
  GVariant *child;
5584
0
  g_autoptr (GBytes) signatures = NULL;
5585
5586
0
  if (metadata)
5587
0
    signaturedata = g_variant_lookup_value (metadata, _OSTREE_METADATA_GPGSIGS_NAME,
5588
0
                                            _OSTREE_METADATA_GPGSIGS_TYPE);
5589
0
  if (!signaturedata)
5590
0
    {
5591
0
      g_set_error_literal (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE,
5592
0
                           "GPG verification enabled, but no signatures found (use "
5593
0
                           "gpg-verify=false in remote config to disable)");
5594
0
      return NULL;
5595
0
    }
5596
5597
  /* OpenPGP data is organized into binary records called packets.  RFC 4880
5598
   * defines a packet as a chunk of data that has a tag specifying its meaning,
5599
   * and consists of a packet header followed by a packet body.  Each packet
5600
   * encodes its own length, and so packets can be concatenated to construct
5601
   * OpenPGP messages, keyrings, or in this case, detached signatures.
5602
   *
5603
   * Each binary blob in the GVariant list is a complete signature packet, so
5604
   * we can concatenate them together to verify all the signatures at once. */
5605
0
  buffer = g_byte_array_new ();
5606
0
  g_variant_iter_init (&iter, signaturedata);
5607
0
  while ((child = g_variant_iter_next_value (&iter)) != NULL)
5608
0
    {
5609
0
      g_byte_array_append (buffer, g_variant_get_data (child), g_variant_get_size (child));
5610
0
      g_variant_unref (child);
5611
0
    }
5612
0
  signatures = g_byte_array_free_to_bytes (buffer);
5613
5614
0
  return _ostree_repo_gpg_verify_data_internal (self, remote_name, signed_data, signatures,
5615
0
                                                keyringdir, extra_keyring, cancellable, error);
5616
0
}
5617
5618
/* Needed an internal version for the remote_name parameter. */
5619
OstreeGpgVerifyResult *
5620
_ostree_repo_verify_commit_internal (OstreeRepo *self, const char *commit_checksum,
5621
                                     const char *remote_name, GFile *keyringdir,
5622
                                     GFile *extra_keyring, GCancellable *cancellable,
5623
                                     GError **error)
5624
0
{
5625
0
  g_autoptr (GVariant) commit_variant = NULL;
5626
  /* Load the commit */
5627
0
  if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum, &commit_variant,
5628
0
                                 error))
5629
0
    return glnx_prefix_error_null (error, "Failed to read commit");
5630
5631
  /* Load the metadata */
5632
0
  g_autoptr (GVariant) metadata = NULL;
5633
0
  if (!ostree_repo_read_commit_detached_metadata (self, commit_checksum, &metadata, cancellable,
5634
0
                                                  error))
5635
0
    return glnx_prefix_error_null (error, "Failed to read detached metadata");
5636
5637
0
  g_autoptr (GBytes) signed_data = g_variant_get_data_as_bytes (commit_variant);
5638
5639
  /* XXX This is a hackish way to indicate to use ALL remote-specific
5640
   *     keyrings in the signature verification.  We want this when
5641
   *     verifying a signed commit that's already been pulled. */
5642
0
  if (remote_name == NULL)
5643
0
    remote_name = OSTREE_ALL_REMOTES;
5644
5645
0
  return _ostree_repo_gpg_verify_with_metadata (self, signed_data, metadata, remote_name,
5646
0
                                                keyringdir, extra_keyring, cancellable, error);
5647
0
}
5648
#endif /* OSTREE_DISABLE_GPGME */
5649
5650
/**
5651
 * ostree_repo_verify_commit:
5652
 * @self: Repository
5653
 * @commit_checksum: ASCII SHA256 checksum
5654
 * @keyringdir: (allow-none): Path to directory GPG keyrings; overrides built-in default if given
5655
 * @extra_keyring: (allow-none): Path to additional keyring file (not a directory)
5656
 * @cancellable: Cancellable
5657
 * @error: Error
5658
 *
5659
 * Check for a valid GPG signature on commit named by the ASCII
5660
 * checksum @commit_checksum.
5661
 *
5662
 * Returns: %TRUE if there was a GPG signature from a trusted keyring, otherwise %FALSE
5663
 */
5664
gboolean
5665
ostree_repo_verify_commit (OstreeRepo *self, const gchar *commit_checksum, GFile *keyringdir,
5666
                           GFile *extra_keyring, GCancellable *cancellable, GError **error)
5667
0
{
5668
0
#ifndef OSTREE_DISABLE_GPGME
5669
0
  g_autoptr (OstreeGpgVerifyResult) result = NULL;
5670
5671
0
  result = ostree_repo_verify_commit_ext (self, commit_checksum, keyringdir, extra_keyring,
5672
0
                                          cancellable, error);
5673
5674
0
  if (!ostree_gpg_verify_result_require_valid_signature (result, error))
5675
0
    return glnx_prefix_error (error, "Commit %s", commit_checksum);
5676
0
  return TRUE;
5677
#else
5678
  /* FIXME: Return false until refactoring */
5679
  return glnx_throw (error, "GPG feature is disabled in a build time");
5680
#endif /* OSTREE_DISABLE_GPGME */
5681
0
}
5682
5683
/**
5684
 * ostree_repo_verify_commit_ext:
5685
 * @self: Repository
5686
 * @commit_checksum: ASCII SHA256 checksum
5687
 * @keyringdir: (allow-none): Path to directory GPG keyrings; overrides built-in default if given
5688
 * @extra_keyring: (allow-none): Path to additional keyring file (not a directory)
5689
 * @cancellable: Cancellable
5690
 * @error: Error
5691
 *
5692
 * Read GPG signature(s) on the commit named by the ASCII checksum
5693
 * @commit_checksum and return detailed results.
5694
 *
5695
 * Returns: (transfer full): an #OstreeGpgVerifyResult, or %NULL on error
5696
 */
5697
OstreeGpgVerifyResult *
5698
ostree_repo_verify_commit_ext (OstreeRepo *self, const gchar *commit_checksum, GFile *keyringdir,
5699
                               GFile *extra_keyring, GCancellable *cancellable, GError **error)
5700
0
{
5701
0
#ifndef OSTREE_DISABLE_GPGME
5702
0
  return _ostree_repo_verify_commit_internal (self, commit_checksum, NULL, keyringdir,
5703
0
                                              extra_keyring, cancellable, error);
5704
#else
5705
  glnx_throw (error, "GPG feature is disabled in a build time");
5706
  return NULL;
5707
#endif /* OSTREE_DISABLE_GPGME */
5708
0
}
5709
5710
/**
5711
 * ostree_repo_verify_commit_for_remote:
5712
 * @self: Repository
5713
 * @commit_checksum: ASCII SHA256 checksum
5714
 * @remote_name: OSTree remote to use for configuration
5715
 * @cancellable: Cancellable
5716
 * @error: Error
5717
 *
5718
 * Read GPG signature(s) on the commit named by the ASCII checksum
5719
 * @commit_checksum and return detailed results, based on the keyring
5720
 * configured for @remote.
5721
 *
5722
 * Returns: (transfer full): an #OstreeGpgVerifyResult, or %NULL on error
5723
 *
5724
 * Since: 2016.14
5725
 */
5726
OstreeGpgVerifyResult *
5727
ostree_repo_verify_commit_for_remote (OstreeRepo *self, const gchar *commit_checksum,
5728
                                      const gchar *remote_name, GCancellable *cancellable,
5729
                                      GError **error)
5730
0
{
5731
0
#ifndef OSTREE_DISABLE_GPGME
5732
0
  return _ostree_repo_verify_commit_internal (self, commit_checksum, remote_name, NULL, NULL,
5733
0
                                              cancellable, error);
5734
#else
5735
  glnx_throw (error, "GPG feature is disabled in a build time");
5736
  return NULL;
5737
#endif /* OSTREE_DISABLE_GPGME */
5738
0
}
5739
5740
/**
5741
 * ostree_repo_gpg_verify_data:
5742
 * @self: Repository
5743
 * @remote_name: (nullable): Name of remote
5744
 * @data: Data as a #GBytes
5745
 * @signatures: Signatures as a #GBytes
5746
 * @keyringdir: (nullable): Path to directory GPG keyrings; overrides built-in default if given
5747
 * @extra_keyring: (nullable): Path to additional keyring file (not a directory)
5748
 * @cancellable: Cancellable
5749
 * @error: Error
5750
 *
5751
 * Verify @signatures for @data using GPG keys in the keyring for
5752
 * @remote_name, and return an #OstreeGpgVerifyResult.
5753
 *
5754
 * The @remote_name parameter can be %NULL. In that case it will do
5755
 * the verifications using GPG keys in the keyrings of all remotes.
5756
 *
5757
 * Returns: (transfer full): an #OstreeGpgVerifyResult, or %NULL on error
5758
 *
5759
 * Since: 2016.6
5760
 */
5761
OstreeGpgVerifyResult *
5762
ostree_repo_gpg_verify_data (OstreeRepo *self, const gchar *remote_name, GBytes *data,
5763
                             GBytes *signatures, GFile *keyringdir, GFile *extra_keyring,
5764
                             GCancellable *cancellable, GError **error)
5765
0
{
5766
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
5767
0
  g_return_val_if_fail (data != NULL, NULL);
5768
0
  g_return_val_if_fail (signatures != NULL, NULL);
5769
5770
0
#ifndef OSTREE_DISABLE_GPGME
5771
0
  return _ostree_repo_gpg_verify_data_internal (
5772
0
      self, (remote_name != NULL) ? remote_name : OSTREE_ALL_REMOTES, data, signatures, keyringdir,
5773
0
      extra_keyring, cancellable, error);
5774
#else
5775
  glnx_throw (error, "GPG feature is disabled in a build time");
5776
  return NULL;
5777
#endif /* OSTREE_DISABLE_GPGME */
5778
0
}
5779
5780
/**
5781
 * ostree_repo_verify_summary:
5782
 * @self: Repo
5783
 * @remote_name: Name of remote
5784
 * @summary: Summary data as a #GBytes
5785
 * @signatures: Summary signatures as a #GBytes
5786
 * @cancellable: Cancellable
5787
 * @error: Error
5788
 *
5789
 * Verify @signatures for @summary data using GPG keys in the keyring for
5790
 * @remote_name, and return an #OstreeGpgVerifyResult.
5791
 *
5792
 * Returns: (transfer full): an #OstreeGpgVerifyResult, or %NULL on error
5793
 */
5794
OstreeGpgVerifyResult *
5795
ostree_repo_verify_summary (OstreeRepo *self, const char *remote_name, GBytes *summary,
5796
                            GBytes *signatures, GCancellable *cancellable, GError **error)
5797
0
{
5798
0
  g_autoptr (GVariant) signatures_variant = NULL;
5799
5800
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
5801
0
  g_return_val_if_fail (remote_name != NULL, NULL);
5802
0
  g_return_val_if_fail (summary != NULL, NULL);
5803
0
  g_return_val_if_fail (signatures != NULL, NULL);
5804
5805
0
  signatures_variant
5806
0
      = g_variant_new_from_bytes (OSTREE_SUMMARY_SIG_GVARIANT_FORMAT, signatures, FALSE);
5807
5808
0
#ifndef OSTREE_DISABLE_GPGME
5809
0
  return _ostree_repo_gpg_verify_with_metadata (self, summary, signatures_variant, remote_name,
5810
0
                                                NULL, NULL, cancellable, error);
5811
#else
5812
  glnx_throw (error, "GPG feature is disabled in a build time");
5813
  return NULL;
5814
#endif /* OSTREE_DISABLE_GPGME */
5815
0
}
5816
5817
/* Add an entry for a @ref ↦ @checksum mapping to an `a(s(t@ay@a{sv}))`
5818
 * @refs_builder to go into a `summary` file. This includes building the
5819
 * standard additional metadata keys for the ref. */
5820
static gboolean
5821
summary_add_ref_entry (OstreeRepo *self, const char *ref, const char *checksum,
5822
                       GVariantBuilder *refs_builder, GError **error)
5823
0
{
5824
0
  g_auto (GVariantDict) commit_metadata_builder = OT_VARIANT_BUILDER_INITIALIZER;
5825
5826
0
  g_assert (ref);
5827
0
  g_assert (checksum);
5828
5829
0
  g_autofree char *remotename = NULL;
5830
0
  if (!ostree_parse_refspec (ref, &remotename, NULL, NULL))
5831
0
    g_assert_not_reached ();
5832
5833
  /* Don't put remote refs in the summary */
5834
0
  if (remotename != NULL)
5835
0
    return TRUE;
5836
5837
0
  g_autoptr (GVariant) commit_obj = NULL;
5838
0
  if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, checksum, &commit_obj, error))
5839
0
    return FALSE;
5840
0
  g_autoptr (GVariant) orig_metadata = g_variant_get_child_value (commit_obj, 0);
5841
5842
0
  g_variant_dict_init (&commit_metadata_builder, NULL);
5843
5844
  /* Forward the commit’s timestamp and version if they're valid. */
5845
0
  guint64 commit_timestamp = ostree_commit_get_timestamp (commit_obj);
5846
0
  g_autoptr (GDateTime) dt = g_date_time_new_from_unix_utc (commit_timestamp);
5847
5848
0
  if (dt != NULL)
5849
0
    g_variant_dict_insert_value (&commit_metadata_builder, OSTREE_COMMIT_TIMESTAMP,
5850
0
                                 g_variant_new_uint64 (GUINT64_TO_BE (commit_timestamp)));
5851
5852
0
  const char *version = NULL;
5853
0
  if (g_variant_lookup (orig_metadata, OSTREE_COMMIT_META_KEY_VERSION, "&s", &version))
5854
0
    g_variant_dict_insert (&commit_metadata_builder, OSTREE_COMMIT_VERSION, "s", version);
5855
5856
0
  g_variant_builder_add_value (
5857
0
      refs_builder, g_variant_new ("(s(t@ay@a{sv}))", ref, (guint64)g_variant_get_size (commit_obj),
5858
0
                                   ostree_checksum_to_bytes_v (checksum),
5859
0
                                   g_variant_dict_end (&commit_metadata_builder)));
5860
5861
0
  return TRUE;
5862
0
}
5863
5864
static gboolean
5865
regenerate_metadata (OstreeRepo *self, gboolean do_metadata_commit, GVariant *additional_metadata,
5866
                     GVariant *options, GCancellable *cancellable, GError **error)
5867
0
{
5868
0
  g_autoptr (OstreeRepoAutoLock) lock = NULL;
5869
0
  gboolean no_deltas_in_summary = FALSE;
5870
5871
0
  lock = ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_SHARED, cancellable, error);
5872
0
  if (!lock)
5873
0
    return FALSE;
5874
5875
  /* Parse options vardict. */
5876
0
  g_autofree char **gpg_key_ids = NULL;
5877
0
  const char *gpg_homedir = NULL;
5878
0
  g_autoptr (GVariant) sign_keys = NULL;
5879
0
  const char *sign_type = NULL;
5880
0
  g_autoptr (OstreeSign) sign = NULL;
5881
5882
0
  if (options != NULL)
5883
0
    {
5884
0
      if (!g_variant_is_of_type (options, G_VARIANT_TYPE_VARDICT))
5885
0
        return glnx_throw (error, "Invalid options doesn't match variant type '%s'",
5886
0
                           (const char *)G_VARIANT_TYPE_VARDICT);
5887
5888
0
      (void)g_variant_lookup (options, "gpg-key-ids", "^a&s", &gpg_key_ids);
5889
0
      (void)g_variant_lookup (options, "gpg-homedir", "&s", &gpg_homedir);
5890
0
      sign_keys = g_variant_lookup_value (options, "sign-keys", G_VARIANT_TYPE_ARRAY);
5891
0
      (void)g_variant_lookup (options, "sign-type", "&s", &sign_type);
5892
5893
0
      if (sign_keys != NULL)
5894
0
        {
5895
0
          if (sign_type == NULL)
5896
0
            sign_type = OSTREE_SIGN_NAME_ED25519;
5897
5898
0
          sign = ostree_sign_get_by_name (sign_type, error);
5899
0
          if (sign == NULL)
5900
0
            return FALSE;
5901
0
        }
5902
0
    }
5903
5904
0
  const gchar *main_collection_id = ostree_repo_get_collection_id (self);
5905
5906
  /* Write out a new metadata commit for the repository when it has a collection ID. */
5907
0
  if (do_metadata_commit && main_collection_id != NULL)
5908
0
    {
5909
0
      g_autoptr (OstreeRepoAutoTransaction) txn
5910
0
          = _ostree_repo_auto_transaction_start (self, cancellable, error);
5911
0
      if (!txn)
5912
0
        return FALSE;
5913
5914
      /* Disable automatic summary updating since we're already doing it */
5915
0
      self->txn.disable_auto_summary = TRUE;
5916
5917
0
      g_autofree gchar *new_ostree_metadata_checksum = NULL;
5918
0
      if (!_ostree_repo_transaction_write_repo_metadata (
5919
0
              self, additional_metadata, &new_ostree_metadata_checksum, cancellable, error))
5920
0
        return FALSE;
5921
5922
      /* Sign the new commit. */
5923
0
      if (gpg_key_ids != NULL)
5924
0
        {
5925
0
          for (const char *const *iter = (const char *const *)gpg_key_ids;
5926
0
               iter != NULL && *iter != NULL; iter++)
5927
0
            {
5928
0
              const char *gpg_key_id = *iter;
5929
5930
0
              if (!ostree_repo_sign_commit (self, new_ostree_metadata_checksum, gpg_key_id,
5931
0
                                            gpg_homedir, cancellable, error))
5932
0
                return FALSE;
5933
0
            }
5934
0
        }
5935
5936
0
      if (sign_keys != NULL)
5937
0
        {
5938
0
          GVariantIter *iter;
5939
0
          GVariant *key;
5940
5941
0
          g_variant_get (sign_keys, "av", &iter);
5942
0
          while (g_variant_iter_loop (iter, "v", &key))
5943
0
            {
5944
0
              if (!ostree_sign_set_sk (sign, key, error))
5945
0
                return FALSE;
5946
5947
0
              if (!ostree_sign_commit (sign, self, new_ostree_metadata_checksum, cancellable,
5948
0
                                       error))
5949
0
                return FALSE;
5950
0
            }
5951
0
        }
5952
5953
0
      if (!_ostree_repo_auto_transaction_commit (txn, NULL, cancellable, error))
5954
0
        return FALSE;
5955
0
    }
5956
5957
0
  g_auto (GVariantDict) additional_metadata_builder = OT_VARIANT_BUILDER_INITIALIZER;
5958
0
  g_variant_dict_init (&additional_metadata_builder, additional_metadata);
5959
0
  g_autoptr (GVariantBuilder) refs_builder
5960
0
      = g_variant_builder_new (G_VARIANT_TYPE ("a(s(taya{sv}))"));
5961
5962
0
  {
5963
0
    if (main_collection_id == NULL)
5964
0
      {
5965
0
        g_autoptr (GHashTable) refs = NULL;
5966
0
        if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error))
5967
0
          return FALSE;
5968
5969
0
        g_autoptr (GList) ordered_keys = g_hash_table_get_keys (refs);
5970
0
        ordered_keys = g_list_sort (ordered_keys, (GCompareFunc)strcmp);
5971
5972
0
        for (GList *iter = ordered_keys; iter; iter = iter->next)
5973
0
          {
5974
0
            const char *ref = iter->data;
5975
0
            const char *commit = g_hash_table_lookup (refs, ref);
5976
5977
0
            if (!summary_add_ref_entry (self, ref, commit, refs_builder, error))
5978
0
              return FALSE;
5979
0
          }
5980
0
      }
5981
0
  }
5982
5983
0
  if (!ot_keyfile_get_boolean_with_default (self->config, "core", "no-deltas-in-summary", FALSE,
5984
0
                                            &no_deltas_in_summary, error))
5985
0
    return FALSE;
5986
5987
0
  if (!no_deltas_in_summary)
5988
0
    {
5989
0
      g_autoptr (GPtrArray) delta_names = NULL;
5990
0
      g_auto (GVariantDict) deltas_builder = OT_VARIANT_BUILDER_INITIALIZER;
5991
5992
0
      if (!ostree_repo_list_static_delta_names (self, &delta_names, cancellable, error))
5993
0
        return FALSE;
5994
5995
0
      g_variant_dict_init (&deltas_builder, NULL);
5996
0
      for (guint i = 0; i < delta_names->len; i++)
5997
0
        {
5998
0
          g_autofree char *from = NULL;
5999
0
          g_autofree char *to = NULL;
6000
0
          GVariant *digest;
6001
6002
0
          if (!_ostree_parse_delta_name (delta_names->pdata[i], &from, &to, error))
6003
0
            return FALSE;
6004
6005
0
          digest = _ostree_repo_static_delta_superblock_digest (
6006
0
              self, (from && from[0]) ? from : NULL, to, cancellable, error);
6007
0
          if (digest == NULL)
6008
0
            return FALSE;
6009
6010
0
          g_variant_dict_insert_value (&deltas_builder, delta_names->pdata[i], digest);
6011
0
        }
6012
6013
0
      if (delta_names->len > 0)
6014
0
        g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_STATIC_DELTAS,
6015
0
                                     g_variant_dict_end (&deltas_builder));
6016
0
    }
6017
6018
0
  {
6019
0
    g_variant_dict_insert_value (
6020
0
        &additional_metadata_builder, OSTREE_SUMMARY_LAST_MODIFIED,
6021
0
        g_variant_new_uint64 (GUINT64_TO_BE (g_get_real_time () / G_USEC_PER_SEC)));
6022
0
  }
6023
6024
0
  {
6025
0
    g_autofree char *remote_mode_str = NULL;
6026
0
    if (!ot_keyfile_get_value_with_default (self->config, "core", "mode", "bare", &remote_mode_str,
6027
0
                                            error))
6028
0
      return FALSE;
6029
0
    g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_MODE,
6030
0
                                 g_variant_new_string (remote_mode_str));
6031
0
  }
6032
6033
0
  {
6034
0
    gboolean tombstone_commits = FALSE;
6035
0
    if (!ot_keyfile_get_boolean_with_default (self->config, "core", "tombstone-commits", FALSE,
6036
0
                                              &tombstone_commits, error))
6037
0
      return FALSE;
6038
0
    g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_TOMBSTONE_COMMITS,
6039
0
                                 g_variant_new_boolean (tombstone_commits));
6040
0
  }
6041
6042
0
  g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_INDEXED_DELTAS,
6043
0
                               g_variant_new_boolean (TRUE));
6044
6045
  /* Add refs which have a collection specified, which could be in refs/mirrors,
6046
   * refs/heads, and/or refs/remotes. */
6047
0
  {
6048
0
    g_autoptr (GHashTable) collection_refs = NULL;
6049
0
    if (!ostree_repo_list_collection_refs (self, NULL, &collection_refs,
6050
0
                                           OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error))
6051
0
      return FALSE;
6052
6053
0
    gsize collection_map_size = 0;
6054
0
    GHashTableIter iter;
6055
0
    g_autoptr (GHashTable) collection_map = NULL; /* (element-type utf8 GHashTable) */
6056
0
    g_hash_table_iter_init (&iter, collection_refs);
6057
0
    collection_map
6058
0
        = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_hash_table_unref);
6059
6060
0
    const OstreeCollectionRef *c_ref;
6061
0
    const char *checksum;
6062
0
    while (g_hash_table_iter_next (&iter, (gpointer *)&c_ref, (gpointer *)&checksum))
6063
0
      {
6064
0
        GHashTable *ref_map = g_hash_table_lookup (collection_map, c_ref->collection_id);
6065
6066
0
        if (ref_map == NULL)
6067
0
          {
6068
0
            ref_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
6069
0
            g_hash_table_insert (collection_map, c_ref->collection_id, ref_map);
6070
0
          }
6071
6072
0
        g_hash_table_insert (ref_map, c_ref->ref_name, (gpointer)checksum);
6073
0
      }
6074
6075
0
    g_autoptr (GVariantBuilder) collection_refs_builder
6076
0
        = g_variant_builder_new (G_VARIANT_TYPE ("a{sa(s(taya{sv}))}"));
6077
6078
0
    g_autoptr (GList) ordered_collection_ids = g_hash_table_get_keys (collection_map);
6079
0
    ordered_collection_ids = g_list_sort (ordered_collection_ids, (GCompareFunc)strcmp);
6080
6081
0
    for (GList *collection_iter = ordered_collection_ids; collection_iter;
6082
0
         collection_iter = collection_iter->next)
6083
0
      {
6084
0
        const char *collection_id = collection_iter->data;
6085
0
        GHashTable *ref_map = g_hash_table_lookup (collection_map, collection_id);
6086
6087
        /* We put the local repo's collection ID in the main refs map, rather
6088
         * than the collection map, for backwards compatibility. */
6089
0
        gboolean is_main_collection_id
6090
0
            = (main_collection_id != NULL && g_str_equal (collection_id, main_collection_id));
6091
6092
0
        if (!is_main_collection_id)
6093
0
          {
6094
0
            g_variant_builder_open (collection_refs_builder, G_VARIANT_TYPE ("{sa(s(taya{sv}))}"));
6095
0
            g_variant_builder_add (collection_refs_builder, "s", collection_id);
6096
0
            g_variant_builder_open (collection_refs_builder, G_VARIANT_TYPE ("a(s(taya{sv}))"));
6097
0
          }
6098
6099
0
        g_autoptr (GList) ordered_refs = g_hash_table_get_keys (ref_map);
6100
0
        ordered_refs = g_list_sort (ordered_refs, (GCompareFunc)strcmp);
6101
6102
0
        for (GList *ref_iter = ordered_refs; ref_iter != NULL; ref_iter = ref_iter->next)
6103
0
          {
6104
0
            const char *ref = ref_iter->data;
6105
0
            const char *commit = g_hash_table_lookup (ref_map, ref);
6106
0
            GVariantBuilder *builder
6107
0
                = is_main_collection_id ? refs_builder : collection_refs_builder;
6108
6109
0
            if (!summary_add_ref_entry (self, ref, commit, builder, error))
6110
0
              return FALSE;
6111
6112
0
            if (!is_main_collection_id)
6113
0
              collection_map_size++;
6114
0
          }
6115
6116
0
        if (!is_main_collection_id)
6117
0
          {
6118
0
            g_variant_builder_close (collection_refs_builder); /* array */
6119
0
            g_variant_builder_close (collection_refs_builder); /* dict entry */
6120
0
          }
6121
0
      }
6122
6123
0
    if (main_collection_id != NULL)
6124
0
      g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_COLLECTION_ID,
6125
0
                                   g_variant_new_string (main_collection_id));
6126
0
    if (collection_map_size > 0)
6127
0
      g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_COLLECTION_MAP,
6128
0
                                   g_variant_builder_end (collection_refs_builder));
6129
0
  }
6130
6131
0
  g_autoptr (GVariant) summary = NULL;
6132
0
  {
6133
0
    g_autoptr (GVariantBuilder) summary_builder
6134
0
        = g_variant_builder_new (OSTREE_SUMMARY_GVARIANT_FORMAT);
6135
6136
0
    g_variant_builder_add_value (summary_builder, g_variant_builder_end (refs_builder));
6137
0
    g_variant_builder_add_value (summary_builder,
6138
0
                                 g_variant_dict_end (&additional_metadata_builder));
6139
0
    summary = g_variant_builder_end (summary_builder);
6140
0
    g_variant_ref_sink (summary);
6141
0
  }
6142
6143
0
  if (!ostree_repo_static_delta_reindex (self, 0, NULL, cancellable, error))
6144
0
    return FALSE;
6145
6146
  /* Create the summary and signature in a temporary directory so that
6147
   * the summary isn't published without a matching signature.
6148
   */
6149
0
  g_auto (GLnxTmpDir) summary_tmpdir = {
6150
0
    0,
6151
0
  };
6152
0
  if (!glnx_mkdtempat (self->tmp_dir_fd, "summary-XXXXXX", 0777, &summary_tmpdir, error))
6153
0
    return FALSE;
6154
0
  g_debug ("Using summary tmpdir %s", summary_tmpdir.path);
6155
6156
0
  if (!_ostree_repo_file_replace_contents (self, summary_tmpdir.fd, "summary",
6157
0
                                           g_variant_get_data (summary),
6158
0
                                           g_variant_get_size (summary), cancellable, error))
6159
0
    return FALSE;
6160
6161
0
  if (gpg_key_ids != NULL
6162
0
      && !_ostree_repo_add_gpg_signature_summary_at (
6163
0
          self, summary_tmpdir.fd, (const char **)gpg_key_ids, gpg_homedir, cancellable, error))
6164
0
    return FALSE;
6165
6166
0
  if (sign_keys != NULL
6167
0
      && !_ostree_sign_summary_at (sign, self, summary_tmpdir.fd, sign_keys, cancellable, error))
6168
0
    return FALSE;
6169
6170
  /* If a signature was made, sync the summary times to it. This way an
6171
   * HTTP client will consider the files expired at the same time.
6172
   */
6173
0
  if (gpg_key_ids != NULL || sign_keys != NULL)
6174
0
    {
6175
0
      struct stat stbuf;
6176
0
      if (!glnx_fstatat (summary_tmpdir.fd, "summary.sig", &stbuf, AT_SYMLINK_NOFOLLOW, error))
6177
0
        return glnx_prefix_error (error, "Unable to get summary.sig status");
6178
6179
0
      struct timespec ts[2];
6180
0
      ts[0] = stbuf.st_atim;
6181
0
      ts[1] = stbuf.st_mtim;
6182
0
      if (TEMP_FAILURE_RETRY (utimensat (summary_tmpdir.fd, "summary", ts, AT_SYMLINK_NOFOLLOW))
6183
0
          != 0)
6184
0
        return glnx_throw_errno_prefix (error, "Unable to change summary timestamps");
6185
0
    }
6186
6187
  /* Rename them into place */
6188
0
  if (!glnx_renameat (summary_tmpdir.fd, "summary", self->repo_dir_fd, "summary", error))
6189
0
    return glnx_prefix_error (error, "Unable to rename summary file: ");
6190
6191
0
  if (gpg_key_ids != NULL || sign_keys != NULL)
6192
0
    {
6193
0
      if (!glnx_renameat (summary_tmpdir.fd, "summary.sig", self->repo_dir_fd, "summary.sig",
6194
0
                          error))
6195
0
        {
6196
          /* Delete an existing signature since it no longer corresponds
6197
           * to the published summary.
6198
           */
6199
0
          g_debug ("Deleting existing unmatched summary.sig file");
6200
0
          (void)ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", NULL);
6201
6202
0
          return glnx_prefix_error (error, "Unable to rename summary signature file: ");
6203
0
        }
6204
0
    }
6205
0
  else
6206
0
    {
6207
0
      g_debug ("Deleting existing unmatched summary.sig file");
6208
0
      if (!ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", error))
6209
0
        return glnx_prefix_error (error, "Unable to delete summary signature file: ");
6210
0
    }
6211
6212
0
  return TRUE;
6213
0
}
6214
6215
/**
6216
 * ostree_repo_regenerate_summary:
6217
 * @self: Repo
6218
 * @additional_metadata: (allow-none): A GVariant of type a{sv}, or %NULL
6219
 * @cancellable: Cancellable
6220
 * @error: Error
6221
 *
6222
 * An OSTree repository can contain a high level "summary" file that
6223
 * describes the available branches and other metadata.
6224
 *
6225
 * If the timetable for making commits and updating the summary file is fairly
6226
 * regular, setting the `ostree.summary.expires` key in @additional_metadata
6227
 * will aid clients in working out when to check for updates.
6228
 *
6229
 * It is regenerated automatically after any ref is
6230
 * added, removed, or updated if `core/auto-update-summary` is set.
6231
 *
6232
 * If the `core/collection-id` key is set in the configuration, it will be
6233
 * included as %OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs that
6234
 * have associated collection IDs will be included in the generated summary
6235
 * file, listed under the %OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs
6236
 * and refs in %OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in
6237
 * lexicographic order.
6238
 *
6239
 * Locking: shared (Prior to 2021.7, this was exclusive)
6240
 */
6241
gboolean
6242
ostree_repo_regenerate_summary (OstreeRepo *self, GVariant *additional_metadata,
6243
                                GCancellable *cancellable, GError **error)
6244
0
{
6245
0
  return regenerate_metadata (self, FALSE, additional_metadata, NULL, cancellable, error);
6246
0
}
6247
6248
/**
6249
 * ostree_repo_regenerate_metadata:
6250
 * @self: Repo
6251
 * @additional_metadata: (nullable): A GVariant `a{sv}`, or %NULL
6252
 * @options: (nullable): A GVariant `a{sv}` with an extensible set of flags
6253
 * @cancellable: Cancellable
6254
 * @error: Error
6255
 *
6256
 * Regenerate the OSTree repository metadata used by clients to describe
6257
 * available branches and other metadata.
6258
 *
6259
 * The repository metadata currently consists of the `summary` file. See
6260
 * ostree_repo_regenerate_summary() and %OSTREE_SUMMARY_GVARIANT_FORMAT for
6261
 * additional details on its contents.
6262
 *
6263
 * Additionally, if the `core/collection-id` key is set in the configuration, a
6264
 * %OSTREE_REPO_METADATA_REF commit will be created.
6265
 *
6266
 * The following @options are currently defined:
6267
 *
6268
 *   * `gpg-key-ids` (`as`): Array of GPG key IDs to sign the metadata with.
6269
 *   * `gpg-homedir` (`s`): GPG home directory.
6270
 *   * `sign-keys` (`av`): Array of keys to sign the metadata with. The key
6271
 *   type is specific to the sign engine used.
6272
 *   * `sign-type` (`s`): Sign engine type to use. If not specified,
6273
 *   %OSTREE_SIGN_NAME_ED25519 is used.
6274
 *
6275
 * Locking: shared
6276
 *
6277
 * Since: 2023.1
6278
 */
6279
gboolean
6280
ostree_repo_regenerate_metadata (OstreeRepo *self, GVariant *additional_metadata, GVariant *options,
6281
                                 GCancellable *cancellable, GError **error)
6282
0
{
6283
0
  return regenerate_metadata (self, TRUE, additional_metadata, options, cancellable, error);
6284
0
}
6285
6286
/* Regenerate the summary if `core/auto-update-summary` is set. We default to FALSE for
6287
 * this setting because OSTree supports multiple processes committing to the same repo (but
6288
 * different refs) concurrently, and in fact gnome-continuous actually does this.  In that
6289
 * context it's best to update the summary explicitly once at the end of multiple
6290
 * transactions instead of automatically here.  `auto-update-summary` only updates
6291
 * atomically within a transaction. */
6292
gboolean
6293
_ostree_repo_maybe_regenerate_summary (OstreeRepo *self, GCancellable *cancellable, GError **error)
6294
0
{
6295
0
  gboolean auto_update_summary;
6296
0
  if (!ot_keyfile_get_boolean_with_default (self->config, "core", "auto-update-summary", FALSE,
6297
0
                                            &auto_update_summary, error))
6298
0
    return FALSE;
6299
6300
  /* Deprecated alias for `auto-update-summary`. */
6301
0
  gboolean commit_update_summary;
6302
0
  if (!ot_keyfile_get_boolean_with_default (self->config, "core", "commit-update-summary", FALSE,
6303
0
                                            &commit_update_summary, error))
6304
0
    return FALSE;
6305
6306
0
  if ((auto_update_summary || commit_update_summary)
6307
0
      && !ostree_repo_regenerate_summary (self, NULL, cancellable, error))
6308
0
    return FALSE;
6309
6310
0
  return TRUE;
6311
0
}
6312
6313
gboolean
6314
_ostree_repo_has_staging_prefix (const char *filename)
6315
0
{
6316
0
  return g_str_has_prefix (filename, OSTREE_REPO_TMPDIR_STAGING);
6317
0
}
6318
6319
gboolean
6320
_ostree_repo_try_lock_tmpdir (int tmpdir_dfd, const char *tmpdir_name, GLnxLockFile *file_lock_out,
6321
                              gboolean *out_did_lock, GError **error)
6322
0
{
6323
0
  g_autofree char *lock_name = g_strconcat (tmpdir_name, "-lock", NULL);
6324
0
  gboolean did_lock = FALSE;
6325
0
  g_autoptr (GError) local_error = NULL;
6326
6327
  /* We put the lock outside the dir, so we can hold the lock
6328
   * until the directory is fully removed */
6329
0
  if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB, file_lock_out, &local_error))
6330
0
    {
6331
      /* we need to handle EACCES too in the case of POSIX locks; see F_SETLK in fcntl(2) */
6332
0
      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)
6333
0
          || g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
6334
0
        {
6335
0
          did_lock = FALSE;
6336
0
        }
6337
0
      else
6338
0
        {
6339
0
          g_propagate_error (error, g_steal_pointer (&local_error));
6340
0
          return FALSE;
6341
0
        }
6342
0
    }
6343
0
  else
6344
0
    {
6345
      /* It's possible that we got a lock after seeing the directory, but
6346
       * another process deleted the tmpdir, so verify it still exists.
6347
       */
6348
0
      struct stat stbuf;
6349
0
      if (!glnx_fstatat_allow_noent (tmpdir_dfd, tmpdir_name, &stbuf, AT_SYMLINK_NOFOLLOW, error))
6350
0
        return FALSE;
6351
0
      if (errno == 0 && S_ISDIR (stbuf.st_mode))
6352
0
        did_lock = TRUE;
6353
0
      else
6354
0
        glnx_release_lock_file (file_lock_out);
6355
0
    }
6356
6357
0
  *out_did_lock = did_lock;
6358
0
  return TRUE;
6359
0
}
6360
6361
/* This allocates and locks a subdir of the repo tmp dir, using an existing
6362
 * one with the same prefix if it is not in use already. */
6363
gboolean
6364
_ostree_repo_allocate_tmpdir (int tmpdir_dfd, const char *tmpdir_prefix, GLnxTmpDir *tmpdir_out,
6365
                              GLnxLockFile *file_lock_out, gboolean *reusing_dir_out,
6366
                              GCancellable *cancellable, GError **error)
6367
0
{
6368
0
  g_return_val_if_fail (_ostree_repo_has_staging_prefix (tmpdir_prefix), FALSE);
6369
6370
  /* Look for existing tmpdir (with same prefix) to reuse */
6371
0
  g_auto (GLnxDirFdIterator) dfd_iter = {
6372
0
    0,
6373
0
  };
6374
0
  if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, ".", FALSE, &dfd_iter, error))
6375
0
    return FALSE;
6376
6377
0
  gboolean reusing_dir = FALSE;
6378
0
  gboolean did_lock = FALSE;
6379
0
  g_auto (GLnxTmpDir) ret_tmpdir = {
6380
0
    0,
6381
0
  };
6382
0
  while (!ret_tmpdir.initialized)
6383
0
    {
6384
0
      struct dirent *dent;
6385
0
      g_autoptr (GError) local_error = NULL;
6386
6387
0
      if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
6388
0
        return FALSE;
6389
6390
0
      if (dent == NULL)
6391
0
        break;
6392
6393
0
      if (!g_str_has_prefix (dent->d_name, tmpdir_prefix))
6394
0
        continue;
6395
6396
      /* Quickly skip non-dirs, if unknown we ignore ENOTDIR when opening instead */
6397
0
      if (dent->d_type != DT_UNKNOWN && dent->d_type != DT_DIR)
6398
0
        continue;
6399
6400
0
      glnx_autofd int target_dfd = -1;
6401
0
      if (!glnx_opendirat (dfd_iter.fd, dent->d_name, FALSE, &target_dfd, &local_error))
6402
0
        {
6403
0
          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)
6404
0
              || g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
6405
0
            continue;
6406
0
          else
6407
0
            {
6408
0
              g_propagate_error (error, g_steal_pointer (&local_error));
6409
0
              return FALSE;
6410
0
            }
6411
0
        }
6412
6413
      /* We put the lock outside the dir, so we can hold the lock
6414
       * until the directory is fully removed */
6415
0
      if (!_ostree_repo_try_lock_tmpdir (tmpdir_dfd, dent->d_name, file_lock_out, &did_lock, error))
6416
0
        return FALSE;
6417
0
      if (!did_lock)
6418
0
        continue;
6419
6420
      /* Touch the reused directory so that we don't accidentally
6421
       * remove it due to being old when cleaning up the tmpdir.
6422
       */
6423
0
      (void)futimens (target_dfd, NULL);
6424
6425
      /* We found an existing tmpdir which we managed to lock */
6426
0
      g_debug ("Reusing tmpdir %s", dent->d_name);
6427
0
      reusing_dir = TRUE;
6428
0
      ret_tmpdir.src_dfd = tmpdir_dfd;
6429
0
      ret_tmpdir.fd = g_steal_fd (&target_dfd);
6430
0
      ret_tmpdir.path = g_strdup (dent->d_name);
6431
0
      ret_tmpdir.initialized = TRUE;
6432
0
    }
6433
6434
0
  const char *tmpdir_name_template = glnx_strjoina (tmpdir_prefix, "XXXXXX");
6435
0
  while (!ret_tmpdir.initialized)
6436
0
    {
6437
0
      g_auto (GLnxTmpDir) new_tmpdir = {
6438
0
        0,
6439
0
      };
6440
      /* No existing tmpdir found, create a new */
6441
0
      if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, DEFAULT_DIRECTORY_MODE, &new_tmpdir,
6442
0
                           error))
6443
0
        return FALSE;
6444
6445
      /* Note, at this point we can race with another process that picks up this
6446
       * new directory. If that happens we need to retry, making a new directory. */
6447
0
      if (!_ostree_repo_try_lock_tmpdir (new_tmpdir.src_dfd, new_tmpdir.path, file_lock_out,
6448
0
                                         &did_lock, error))
6449
0
        return FALSE;
6450
0
      if (!did_lock)
6451
0
        {
6452
          /* We raced and someone else already locked the newly created
6453
           * directory. Free the resources here and then mark it as
6454
           * uninitialized so glnx_tmpdir_cleanup doesn't delete the directory
6455
           * when new_tmpdir goes out of scope.
6456
           */
6457
0
          glnx_tmpdir_unset (&new_tmpdir);
6458
0
          new_tmpdir.initialized = FALSE;
6459
0
          continue;
6460
0
        }
6461
6462
0
      g_debug ("Using new tmpdir %s", new_tmpdir.path);
6463
0
      ret_tmpdir = new_tmpdir; /* Transfer ownership */
6464
0
      new_tmpdir.initialized = FALSE;
6465
0
    }
6466
6467
0
  *tmpdir_out = ret_tmpdir; /* Transfer ownership */
6468
0
  ret_tmpdir.initialized = FALSE;
6469
0
  *reusing_dir_out = reusing_dir;
6470
0
  return TRUE;
6471
0
}
6472
6473
/* See ostree-repo-private.h for more information about this */
6474
void
6475
_ostree_repo_memory_cache_ref_init (OstreeRepoMemoryCacheRef *state, OstreeRepo *repo)
6476
0
{
6477
0
  state->repo = g_object_ref (repo);
6478
0
  GMutex *lock = &repo->cache_lock;
6479
0
  g_mutex_lock (lock);
6480
0
  repo->dirmeta_cache_refcount++;
6481
0
  if (repo->dirmeta_cache == NULL)
6482
0
    repo->dirmeta_cache
6483
0
        = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
6484
0
  g_mutex_unlock (lock);
6485
0
}
6486
6487
/* See ostree-repo-private.h for more information about this */
6488
void
6489
_ostree_repo_memory_cache_ref_destroy (OstreeRepoMemoryCacheRef *state)
6490
0
{
6491
0
  OstreeRepo *repo = state->repo;
6492
0
  GMutex *lock = &repo->cache_lock;
6493
0
  g_mutex_lock (lock);
6494
0
  repo->dirmeta_cache_refcount--;
6495
0
  if (repo->dirmeta_cache_refcount == 0)
6496
0
    g_clear_pointer (&repo->dirmeta_cache, g_hash_table_unref);
6497
0
  g_mutex_unlock (lock);
6498
0
  g_object_unref (repo);
6499
0
}
6500
6501
/**
6502
 * ostree_repo_get_collection_id:
6503
 * @self: an #OstreeRepo
6504
 *
6505
 * Get the collection ID of this repository. See [collection IDs][collection-ids].
6506
 *
6507
 * Returns: (nullable): collection ID for the repository
6508
 * Since: 2018.6
6509
 */
6510
const gchar *
6511
ostree_repo_get_collection_id (OstreeRepo *self)
6512
0
{
6513
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
6514
6515
0
  return self->collection_id;
6516
0
}
6517
6518
/**
6519
 * ostree_repo_set_collection_id:
6520
 * @self: an #OstreeRepo
6521
 * @collection_id: (nullable): new collection ID, or %NULL to unset it
6522
 * @error: return location for a #GError, or %NULL
6523
 *
6524
 * Set or clear the collection ID of this repository. See [collection IDs][collection-ids].
6525
 * The update will be made in memory, but must be written out to the repository
6526
 * configuration on disk using ostree_repo_write_config().
6527
 *
6528
 * Returns: %TRUE on success, %FALSE otherwise
6529
 * Since: 2018.6
6530
 */
6531
gboolean
6532
ostree_repo_set_collection_id (OstreeRepo *self, const gchar *collection_id, GError **error)
6533
0
{
6534
0
  if (collection_id != NULL && !ostree_validate_collection_id (collection_id, error))
6535
0
    return FALSE;
6536
6537
0
  g_autofree gchar *new_collection_id = g_strdup (collection_id);
6538
0
  g_free (self->collection_id);
6539
0
  self->collection_id = g_steal_pointer (&new_collection_id);
6540
6541
0
  if (self->config != NULL)
6542
0
    {
6543
0
      if (collection_id != NULL)
6544
0
        g_key_file_set_string (self->config, "core", "collection-id", collection_id);
6545
0
      else
6546
0
        return g_key_file_remove_key (self->config, "core", "collection-id", error);
6547
0
    }
6548
6549
0
  return TRUE;
6550
0
}
6551
6552
/**
6553
 * ostree_repo_get_default_repo_finders:
6554
 * @self: an #OstreeRepo
6555
 *
6556
 * Get the set of default repo finders configured. See the documentation for
6557
 * the "core.default-repo-finders" config key.
6558
 *
6559
 * Returns: (array zero-terminated=1) (element-type utf8):
6560
 *    %NULL-terminated array of strings.
6561
 * Since: 2018.9
6562
 */
6563
const gchar *const *
6564
ostree_repo_get_default_repo_finders (OstreeRepo *self)
6565
0
{
6566
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
6567
6568
0
  return (const gchar *const *)self->repo_finders;
6569
0
}
6570
6571
/**
6572
 * ostree_repo_get_bootloader:
6573
 * @self: an #OstreeRepo
6574
 *
6575
 * Get the bootloader configured. See the documentation for the
6576
 * "sysroot.bootloader" config key.
6577
 *
6578
 * Returns: (transfer none): bootloader configuration for the sysroot
6579
 * Since: 2019.2
6580
 */
6581
const gchar *
6582
ostree_repo_get_bootloader (OstreeRepo *self)
6583
0
{
6584
0
  g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
6585
6586
0
  return CFG_SYSROOT_BOOTLOADER_OPTS_STR[self->bootloader];
6587
0
}
6588
6589
/**
6590
 * _ostree_repo_verify_bindings:
6591
 * @collection_id: (nullable): Locally specified collection ID for the remote
6592
 *    the @commit was retrieved from, or %NULL if none is configured
6593
 * @ref_name: (nullable): Ref name the commit was retrieved using, or %NULL if
6594
 *    the commit was retrieved by checksum
6595
 * @commit: Commit data to check
6596
 * @error: Return location for a #GError, or %NULL
6597
 *
6598
 * Verify the ref and collection bindings.
6599
 *
6600
 * The ref binding is verified only if it exists. But if we have the
6601
 * collection ID specified in the remote configuration (@collection_id is
6602
 * non-%NULL) then the ref binding must exist, otherwise the verification will
6603
 * fail. Parts of the verification can be skipped by passing %NULL to the
6604
 * @ref_name parameter (in case we requested a checksum directly, without
6605
 * looking it up from a ref).
6606
 *
6607
 * The collection binding is verified only when we have collection ID
6608
 * specified in the remote configuration. If it is specified, then the
6609
 * binding must exist and must be equal to the remote repository
6610
 * collection ID.
6611
 *
6612
 * Returns: %TRUE if bindings are correct, %FALSE otherwise
6613
 * Since: 2017.14
6614
 */
6615
gboolean
6616
_ostree_repo_verify_bindings (const char *collection_id, const char *ref_name, GVariant *commit,
6617
                              GError **error)
6618
0
{
6619
0
  g_autoptr (GVariant) metadata = g_variant_get_child_value (commit, 0);
6620
0
  g_autofree const char **refs = NULL;
6621
0
  if (!g_variant_lookup (metadata, OSTREE_COMMIT_META_KEY_REF_BINDING, "^a&s", &refs))
6622
0
    {
6623
      /* Early return here - if the remote collection ID is NULL, then
6624
       * we certainly will not verify the collection binding in the
6625
       * commit.
6626
       */
6627
0
      if (collection_id == NULL)
6628
0
        return TRUE;
6629
6630
0
      return glnx_throw (error, "Expected commit metadata to have ref "
6631
0
                                "binding information, found none");
6632
0
    }
6633
6634
0
  if (ref_name != NULL)
6635
0
    {
6636
0
      if (!g_strv_contains ((const char *const *)refs, ref_name))
6637
0
        {
6638
0
          g_autoptr (GString) refs_dump = g_string_new (NULL);
6639
0
          const char *refs_str;
6640
6641
0
          if (refs != NULL && (*refs) != NULL)
6642
0
            {
6643
0
              for (const char **iter = refs; *iter != NULL; ++iter)
6644
0
                {
6645
0
                  const char *ref = *iter;
6646
6647
0
                  if (refs_dump->len > 0)
6648
0
                    g_string_append (refs_dump, ", ");
6649
0
                  g_string_append_printf (refs_dump, "‘%s’", ref);
6650
0
                }
6651
6652
0
              refs_str = refs_dump->str;
6653
0
            }
6654
0
          else
6655
0
            {
6656
0
              refs_str = "no refs";
6657
0
            }
6658
6659
0
          return glnx_throw (error,
6660
0
                             "Commit has no requested ref ‘%s’ "
6661
0
                             "in ref binding metadata (%s)",
6662
0
                             ref_name, refs_str);
6663
0
        }
6664
0
    }
6665
6666
0
  if (collection_id != NULL)
6667
0
    {
6668
0
      const char *collection_id_binding;
6669
0
      if (!g_variant_lookup (metadata, OSTREE_COMMIT_META_KEY_COLLECTION_BINDING, "&s",
6670
0
                             &collection_id_binding))
6671
0
        return glnx_throw (error, "Expected commit metadata to have collection ID "
6672
0
                                  "binding information, found none");
6673
0
      if (!g_str_equal (collection_id_binding, collection_id))
6674
0
        return glnx_throw (error,
6675
0
                           "Commit has collection ID ‘%s’ in collection binding "
6676
0
                           "metadata, while the remote it came from has "
6677
0
                           "collection ID ‘%s’",
6678
0
                           collection_id_binding, collection_id);
6679
0
    }
6680
6681
0
  return TRUE;
6682
0
}