summaryrefslogtreecommitdiff
path: root/src/libostree/ostree-repo-private.h
blob: f38658548113caa85358b6fd02bc38fae3a53ca0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/*
 * Copyright (C) 2011,2013 Colin Walters <walters@verbum.org>
 *
 * SPDX-License-Identifier: LGPL-2.0+
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <https://www.gnu.org/licenses/>.
 */

#pragma once

#include "config.h"
#include "ostree-ref.h"
#include "ostree-remote-private.h"
#include "ostree-repo.h"
#include "otutil.h"
#include <sys/statvfs.h>

G_BEGIN_DECLS

#define OSTREE_DELTAPART_VERSION (0)

#define _OSTREE_SUMMARY_CACHE_DIR "summaries"
#define _OSTREE_CACHE_DIR "cache"

#define _OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS 8
#define _OSTREE_MAX_OUTSTANDING_DELTAPART_REQUESTS 2

/* We want some parallelism with disk writes, but we also
 * want to avoid starting tens or hundreds of threads
 * (via GTask) all writing to disk.  Eventually we may
 * use io_uring which handles backpressure correctly.
 * Also, in "immediate fsync" mode, this helps provide
 * much more backpressure, helping our I/O patterns
 * be nicer for any concurrent processes, such as etcd
 * or other databases.
 * https://github.com/openshift/machine-config-operator/issues/1897
 * */
#define _OSTREE_MAX_OUTSTANDING_WRITE_REQUESTS 3

/* Well-known keys for the additional metadata field in a summary file. */
#define OSTREE_SUMMARY_LAST_MODIFIED "ostree.summary.last-modified"
#define OSTREE_SUMMARY_EXPIRES "ostree.summary.expires"
#define OSTREE_SUMMARY_COLLECTION_ID "ostree.summary.collection-id"
#define OSTREE_SUMMARY_COLLECTION_MAP "ostree.summary.collection-map"
#define OSTREE_SUMMARY_MODE "ostree.summary.mode"
#define OSTREE_SUMMARY_TOMBSTONE_COMMITS "ostree.summary.tombstone-commits"
#define OSTREE_SUMMARY_INDEXED_DELTAS "ostree.summary.indexed-deltas"

#define _OSTREE_PAYLOAD_LINK_PREFIX "../"
#define _OSTREE_PAYLOAD_LINK_PREFIX_LEN (sizeof (_OSTREE_PAYLOAD_LINK_PREFIX) - 1)

/* Well-known keys for the additional metadata field in a commit in a ref entry
 * in a summary file. */
#define OSTREE_COMMIT_TIMESTAMP "ostree.commit.timestamp"
#define OSTREE_COMMIT_VERSION "ostree.commit.version"

typedef enum
{
  OSTREE_REPO_TEST_ERROR_PRE_COMMIT = (1 << 0),
  OSTREE_REPO_TEST_ERROR_INVALID_CACHE = (1 << 1),
} OstreeRepoTestErrorFlags;

struct OstreeRepoCommitModifier
{
  gint refcount; /* atomic */

  OstreeRepoCommitModifierFlags flags;
  OstreeRepoCommitFilter filter;
  gpointer user_data;
  GDestroyNotify destroy_notify;

  OstreeRepoCommitModifierXattrCallback xattr_callback;
  GDestroyNotify xattr_destroy;
  gpointer xattr_user_data;

  GLnxTmpDir sepolicy_tmpdir;
  OstreeSePolicy *sepolicy;
  GHashTable *devino_cache;
};

typedef enum
{
  OSTREE_REPO_SYSROOT_KIND_UNKNOWN,
  OSTREE_REPO_SYSROOT_KIND_NO,                /* Not a system repo */
  OSTREE_REPO_SYSROOT_KIND_VIA_SYSROOT,       /* Constructed via ostree_sysroot_get_repo() */
  OSTREE_REPO_SYSROOT_KIND_IS_SYSROOT_OSTREE, /* We match /ostree/repo */
} OstreeRepoSysrootKind;

typedef struct
{
  GHashTable *refs;            /* (element-type utf8 utf8) */
  GHashTable *collection_refs; /* (element-type OstreeCollectionRef utf8) */
  OstreeRepoTransactionStats stats;
  /* Implementation of min-free-space-percent */
  gulong blocksize;
  fsblkcnt_t max_blocks;
  gboolean disable_auto_summary;
} OstreeRepoTxn;

typedef struct
{
  GMutex mutex;    /* All other members should only be accessed with this held */
  int fd;          /* The open file or flock file descriptor */
  guint shared;    /* Number of shared locks curently held */
  guint exclusive; /* Number of exclusive locks currently held */
} OstreeRepoLock;

typedef enum
{
  _OSTREE_FEATURE_NO,
  _OSTREE_FEATURE_MAYBE,
  _OSTREE_FEATURE_YES,
} _OstreeFeatureSupport;

/* Possible values for the sysroot.bootloader configuration variable */
typedef enum
{
  CFG_SYSROOT_BOOTLOADER_OPT_AUTO = 0,
  CFG_SYSROOT_BOOTLOADER_OPT_NONE,
  CFG_SYSROOT_BOOTLOADER_OPT_GRUB2,
  CFG_SYSROOT_BOOTLOADER_OPT_SYSLINUX,
  CFG_SYSROOT_BOOTLOADER_OPT_UBOOT,
  CFG_SYSROOT_BOOTLOADER_OPT_ZIPL,
  CFG_SYSROOT_BOOTLOADER_OPT_ABOOT,
  /* Non-exhaustive */
} OstreeCfgSysrootBootloaderOpt;

static const char *const CFG_SYSROOT_BOOTLOADER_OPTS_STR[] = {
  /* This must be kept in the same order as the enum */
  "auto", "none", "grub2", "syslinux", "uboot", "zipl", "aboot", NULL,
};

/**
 * OstreeRepo:
 *
 * Private instance structure.
 */
struct OstreeRepo
{
  GObject parent;

  char *stagedir_prefix;
  GLnxTmpDir commit_stagedir;
  GLnxLockFile commit_stagedir_lock;

  /* A cached fd-relative version, distinct from the case where we may have a
   * user-provided absolute path.
   */
  GFile *repodir_fdrel;
  GFile *repodir; /* May be %NULL if we were opened via ostree_repo_open_at() */
  int repo_dir_fd;
  int tmp_dir_fd;
  int cache_dir_fd;
  char *cache_dir;
  int objects_dir_fd;
  int uncompressed_objects_dir_fd;
  GFile *sysroot_dir;
  GWeakRef sysroot; /* Weak to avoid a circular ref; see also `is_system` */
  char *remotes_config_dir;

  OstreeRepoLock lock;

  GMutex txn_lock;
  OstreeRepoTxn txn;
  gboolean txn_locked;
  _OstreeFeatureSupport fs_verity_wanted;
  _OstreeFeatureSupport fs_verity_supported;

  GMutex cache_lock;
  guint dirmeta_cache_refcount;
  /* char * checksum → GVariant * for dirmeta objects, used in the checkout path */
  GHashTable *dirmeta_cache;

  gboolean inited;
  gboolean writable;
  OstreeRepoSysrootKind sysroot_kind;
  GError *writable_error;
  gboolean in_transaction;
  gboolean disable_fsync;
  gboolean per_object_fsync;
  gboolean disable_xattrs;
  guint zlib_compression_level;
  GHashTable *loose_object_devino_hash;
  GHashTable *updated_uncompressed_dirs;

  /* FIXME: The object sizes hash table is really per-commit state, not repo
   * state. Using a single table for the repo means that commits cannot be
   * built simultaneously if they're adding size information. This data should
   * probably be in OstreeMutableTree, but that's gone by the time the actual
   * commit is constructed. At that point the only commit state is in the root
   * OstreeRepoFile.
   */
  GHashTable *object_sizes;

  /* Cache the repo's device/inode to use for comparisons elsewhere */
  dev_t device;
  ino_t inode;
  uid_t owner_uid;              /* Cache of repo's owner uid */
  guint min_free_space_percent; /* See the min-free-space-percent config option */
  guint64 min_free_space_mb;    /* See the min-free-space-size config option */
  guint64 reserved_blocks;
  gboolean cleanup_stagedir;

  guint test_error_flags; /* OstreeRepoTestErrorFlags */

  GKeyFile *config;
  GHashTable *remotes;
  GMutex remotes_lock;
  OstreeRepoMode mode;
  gboolean enable_uncompressed_cache;
  gboolean generate_sizes;
  guint64 tmp_expiry_seconds;
  gchar *collection_id;
  gboolean add_remotes_config_dir; /* Add new remotes in remotes.d dir */
  gint lock_timeout_seconds;
  guint64 payload_link_threshold;
  gint fs_support_reflink; /* The underlying filesystem has support for ioctl (FICLONE..) */
  gchar **repo_finders;
  OstreeCfgSysrootBootloaderOpt bootloader; /* Configure which bootloader to use. */
  GHashTable
      *bls_append_values;     /* Parsed key-values from bls-append-except-default key in config. */
  gboolean enable_bootprefix; /* If true, prepend bootloader entries with /boot */

  OstreeRepo *parent_repo;
};

typedef struct
{
  dev_t dev;
  ino_t ino;
  char checksum[OSTREE_SHA256_STRING_LEN + 1];
} OstreeDevIno;

/* A MemoryCacheRef is an in-memory cache of objects (currently just DIRMETA).  This can
 * be used when performing an operation that traverses a repository in someway.  Currently,
 * the primary use case is ostree_repo_checkout_at() avoiding lots of duplicate dirmeta
 * lookups.
 */
typedef struct
{
  OstreeRepo *repo;
} OstreeRepoMemoryCacheRef;

void _ostree_repo_memory_cache_ref_init (OstreeRepoMemoryCacheRef *state, OstreeRepo *repo);

void _ostree_repo_memory_cache_ref_destroy (OstreeRepoMemoryCacheRef *state);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoMemoryCacheRef, _ostree_repo_memory_cache_ref_destroy)

#define OSTREE_REPO_TMPDIR_STAGING "staging-"

gboolean _ostree_repo_allocate_tmpdir (int tmpdir_dfd, const char *tmpdir_prefix,
                                       GLnxTmpDir *tmpdir_out, GLnxLockFile *file_lock_out,
                                       gboolean *reusing_dir_out, GCancellable *cancellable,
                                       GError **error);

gboolean _ostree_repo_has_staging_prefix (const char *filename);

gboolean _ostree_repo_try_lock_tmpdir (int tmpdir_dfd, const char *tmpdir_name,
                                       GLnxLockFile *file_lock_out, gboolean *out_did_lock,
                                       GError **error);

gboolean _ostree_repo_ensure_loose_objdir_at (int dfd, const char *loose_path,
                                              GCancellable *cancellable, GError **error);

GFile *_ostree_repo_get_commit_metadata_loose_path (OstreeRepo *self, const char *checksum);

gboolean _ostree_repo_has_loose_object (OstreeRepo *self, const char *checksum,
                                        OstreeObjectType objtype, gboolean *out_is_stored,
                                        GCancellable *cancellable, GError **error);

gboolean _ostree_write_bareuser_metadata (int fd, guint32 uid, guint32 gid, guint32 mode,
                                          GVariant *xattrs, GError **error);

gboolean _ostree_repo_write_directory_meta (OstreeRepo *self, GFileInfo *file_info,
                                            GVariant *xattrs, guchar **out_csum,
                                            GCancellable *cancellable, GError **error);
gboolean _ostree_repo_update_refs (OstreeRepo *self, GHashTable *refs, GCancellable *cancellable,
                                   GError **error);

gboolean _ostree_repo_update_collection_refs (OstreeRepo *self, GHashTable *refs,
                                              GCancellable *cancellable, GError **error);

gboolean _ostree_repo_file_replace_contents (OstreeRepo *self, int dfd, const char *path,
                                             const guint8 *buf, gsize len,
                                             GCancellable *cancellable, GError **error);

gboolean _ostree_repo_write_ref (OstreeRepo *self, const char *remote,
                                 const OstreeCollectionRef *ref, const char *rev, const char *alias,
                                 GCancellable *cancellable, GError **error);

OstreeRepoFile *_ostree_repo_file_new_for_commit (OstreeRepo *repo, const char *commit,
                                                  GError **error);

OstreeRepoFile *_ostree_repo_file_new_root (OstreeRepo *repo, const char *contents_checksum,
                                            const char *metadata_checksum);

gboolean _ostree_repo_traverse_dirtree_internal (OstreeRepo *repo, const char *dirtree_checksum,
                                                 int recursion_depth, GHashTable *inout_reachable,
                                                 GHashTable *inout_content_names,
                                                 GCancellable *cancellable, GError **error);

OstreeRepoCommitFilterResult _ostree_repo_commit_modifier_apply (OstreeRepo *self,
                                                                 OstreeRepoCommitModifier *modifier,
                                                                 const char *path,
                                                                 GFileInfo *file_info,
                                                                 GFileInfo **out_modified_info);

void _ostree_repo_setup_generate_sizes (OstreeRepo *self, OstreeRepoCommitModifier *modifier);

gboolean _ostree_repo_remote_name_is_file (const char *remote_name);

#ifndef OSTREE_DISABLE_GPGME
OstreeGpgVerifyResult *_ostree_repo_gpg_verify_with_metadata (
    OstreeRepo *self, GBytes *signed_data, GVariant *metadata, const char *remote_name,
    GFile *keyringdir, GFile *extra_keyring, GCancellable *cancellable, GError **error);

OstreeGpgVerifyResult *_ostree_repo_verify_commit_internal (
    OstreeRepo *self, const char *commit_checksum, const char *remote_name, GFile *keyringdir,
    GFile *extra_keyring, GCancellable *cancellable, GError **error);
#endif /* OSTREE_DISABLE_GPGME */

typedef enum
{
  _OSTREE_REPO_IMPORT_FLAGS_NONE = 0,
  _OSTREE_REPO_IMPORT_FLAGS_TRUSTED = (1 << 0),
  _OSTREE_REPO_IMPORT_FLAGS_VERIFY_BAREUSERONLY = (1 << 1),
} OstreeRepoImportFlags;

gboolean _ostree_repo_import_object (OstreeRepo *self, OstreeRepo *source, OstreeObjectType objtype,
                                     const char *checksum, OstreeRepoImportFlags flags,
                                     GCancellable *cancellable, GError **error);

gboolean _ostree_repo_commit_tmpf_final (OstreeRepo *self, const char *checksum,
                                         OstreeObjectType objtype, GLnxTmpfile *tmpf,
                                         GCancellable *cancellable, GError **error);

typedef struct
{
  gboolean initialized;
  gpointer opaque0[10];
  guint opaque1[10];
} OstreeRepoBareContent;
void _ostree_repo_bare_content_cleanup (OstreeRepoBareContent *regwrite);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoBareContent, _ostree_repo_bare_content_cleanup)

gboolean _ostree_repo_bare_content_open (OstreeRepo *self, const char *checksum,
                                         guint64 content_len, guint uid, guint gid, guint mode,
                                         GVariant *xattrs, OstreeRepoBareContent *out_regwrite,
                                         GCancellable *cancellable, GError **error);

gboolean _ostree_repo_bare_content_write (OstreeRepo *repo, OstreeRepoBareContent *barewrite,
                                          const guint8 *buf, size_t len, GCancellable *cancellable,
                                          GError **error);

gboolean _ostree_repo_bare_content_commit (OstreeRepo *self, OstreeRepoBareContent *barewrite,
                                           char *checksum_buf, size_t buflen,
                                           GCancellable *cancellable, GError **error);

OstreeContentWriter *_ostree_content_writer_new (OstreeRepo *repo, const char *checksum, guint uid,
                                                 guint gid, guint mode, guint64 content_len,
                                                 GVariant *xattrs, GError **error);

gboolean _ostree_repo_load_file_bare (OstreeRepo *self, const char *checksum, int *out_fd,
                                      struct stat *out_stbuf, char **out_symlink,
                                      GVariant **out_xattrs, GCancellable *cancellable,
                                      GError **error);

gboolean _ostree_repo_update_mtime (OstreeRepo *self, GError **error);

gboolean _ostree_repo_add_remote (OstreeRepo *self, OstreeRemote *remote);
gboolean _ostree_repo_remove_remote (OstreeRepo *self, OstreeRemote *remote);
OstreeRemote *_ostree_repo_get_remote (OstreeRepo *self, const char *name, GError **error);
OstreeRemote *_ostree_repo_get_remote_inherited (OstreeRepo *self, const char *name,
                                                 GError **error);

gboolean _ostree_repo_maybe_regenerate_summary (OstreeRepo *self, GCancellable *cancellable,
                                                GError **error);

gboolean _ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error);

gboolean _ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, _OstreeFeatureSupport fsverity_requested,
                                     gboolean *supported, GError **error);

gboolean _ostree_tmpf_fsverity (OstreeRepo *self, GLnxTmpfile *tmpf, GError **error);

gboolean _ostree_repo_verify_bindings (const char *collection_id, const char *ref_name,
                                       GVariant *commit, GError **error);

GHashTable *ostree_repo_list_objects_set (OstreeRepo *self, OstreeRepoListObjectsFlags flags,
                                          GCancellable *cancellable, GError **error);

gboolean _ostree_repo_transaction_write_repo_metadata (OstreeRepo *self,
                                                       GVariant *additional_metadata,
                                                       char **out_checksum,
                                                       GCancellable *cancellable, GError **error);

/**
 * OstreeRepoAutoTransaction:
 *
 * A transaction guard for a specific #OstreeRepo. It can be explicitly
 * completed through abort/commit. If the guard has not been completed
 * beforehand, on cleanup it is automatically aborted.
 *
 * Taken from flatpak; may be made into public API later
 */
typedef struct
{
  gint atomic_refcount;
  OstreeRepo *repo;
} OstreeRepoAutoTransaction;

OstreeRepoAutoTransaction *
_ostree_repo_auto_transaction_start (OstreeRepo *repo, GCancellable *cancellable, GError **error);

gboolean _ostree_repo_auto_transaction_abort (OstreeRepoAutoTransaction *txn,
                                              GCancellable *cancellable, GError **error);

gboolean _ostree_repo_auto_transaction_commit (OstreeRepoAutoTransaction *txn,
                                               OstreeRepoTransactionStats *out_stats,
                                               GCancellable *cancellable, GError **error);

OstreeRepoAutoTransaction *_ostree_repo_auto_transaction_ref (OstreeRepoAutoTransaction *txn);

void _ostree_repo_auto_transaction_unref (OstreeRepoAutoTransaction *txn);

GType _ostree_repo_auto_transaction_get_type (void);

G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoTransaction, _ostree_repo_auto_transaction_unref);

/* Internal function to break a circular dependency:
 * should not be made into public API, even if the rest is */
OstreeRepoAutoTransaction *_ostree_repo_auto_transaction_new (OstreeRepo *repo);

G_END_DECLS