hc
2024-08-16 a24a44ff9ca902811b99aa9663d697cf452e08ef
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
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2018 Google LLC
 */
 
/*
 * Overview
 * --------
 * The backbone of the incremental-fs ondisk format is an append only linked
 * list of metadata blocks. Each metadata block contains an offset of the next
 * one. These blocks describe files and directories on the
 * file system. They also represent actions of adding and removing file names
 * (hard links).
 *
 * Every time incremental-fs instance is mounted, it reads through this list
 * to recreate filesystem's state in memory. An offset of the first record in
 * the metadata list is stored in the superblock at the beginning of the backing
 * file.
 *
 * Most of the backing file is taken by data areas and blockmaps.
 * Since data blocks can be compressed and have different sizes,
 * single per-file data area can't be pre-allocated. That's why blockmaps are
 * needed in order to find a location and size of each data block in
 * the backing file. Each time a file is created, a corresponding block map is
 * allocated to store future offsets of data blocks.
 *
 * Whenever a data block is given by data loader to incremental-fs:
 *   - A data area with the given block is appended to the end of
 *     the backing file.
 *   - A record in the blockmap for the given block index is updated to reflect
 *     its location, size, and compression algorithm.
 
 * Metadata records
 * ----------------
 * incfs_blockmap - metadata record that specifies size and location
 *                           of a blockmap area for a given file. This area
 *                           contains an array of incfs_blockmap_entry-s.
 * incfs_file_signature - metadata record that specifies where file signature
 *                           and its hash tree can be found in the backing file.
 *
 * incfs_file_attr - metadata record that specifies where additional file
 *                attributes blob can be found.
 *
 * Metadata header
 * ---------------
 * incfs_md_header - header of a metadata record. It's always a part
 *                   of other structures and served purpose of metadata
 *                   bookkeeping.
 *
 *              +-----------------------------------------------+       ^
 *              |            incfs_md_header                    |       |
 *              | 1. type of body(BLOCKMAP, FILE_ATTR..)        |       |
 *              | 2. size of the whole record header + body     |       |
 *              | 3. CRC the whole record header + body         |       |
 *              | 4. offset of the previous md record           |]------+
 *              | 5. offset of the next md record (md link)     |]---+
 *              +-----------------------------------------------+    |
 *              |  Metadata record body with useful data        |    |
 *              +-----------------------------------------------+    |
 *                                                                   +--->
 *
 * Other ondisk structures
 * -----------------------
 * incfs_super_block - backing file header
 * incfs_blockmap_entry - a record in a blockmap area that describes size
 *                       and location of a data block.
 * Data blocks dont have any particular structure, they are written to the
 * backing file in a raw form as they come from a data loader.
 *
 * Backing file layout
 * -------------------
 *
 *
 *              +-------------------------------------------+
 *              |            incfs_file_header              |]---+
 *              +-------------------------------------------+    |
 *              |                 metadata                  |<---+
 *              |           incfs_file_signature            |]---+
 *              +-------------------------------------------+    |
 *                        .........................              |
 *              +-------------------------------------------+    |   metadata
 *     +------->|               blockmap area               |    |  list links
 *     |        |          [incfs_blockmap_entry]           |    |
 *     |        |          [incfs_blockmap_entry]           |    |
 *     |        |          [incfs_blockmap_entry]           |    |
 *     |    +--[|          [incfs_blockmap_entry]           |    |
 *     |    |   |          [incfs_blockmap_entry]           |    |
 *     |    |   |          [incfs_blockmap_entry]           |    |
 *     |    |   +-------------------------------------------+    |
 *     |    |             .........................              |
 *     |    |   +-------------------------------------------+    |
 *     |    |   |                 metadata                  |<---+
 *     +----|--[|               incfs_blockmap              |]---+
 *          |   +-------------------------------------------+    |
 *          |             .........................              |
 *          |   +-------------------------------------------+    |
 *          +-->|                 data block                |    |
 *              +-------------------------------------------+    |
 *                        .........................              |
 *              +-------------------------------------------+    |
 *              |                 metadata                  |<---+
 *              |              incfs_file_attr              |
 *              +-------------------------------------------+
 */
#ifndef _INCFS_FORMAT_H
#define _INCFS_FORMAT_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <uapi/linux/incrementalfs.h>
 
#include "internal.h"
 
#define INCFS_MAX_NAME_LEN 255
#define INCFS_FORMAT_V1 1
#define INCFS_FORMAT_CURRENT_VER INCFS_FORMAT_V1
 
enum incfs_metadata_type {
   INCFS_MD_NONE = 0,
   INCFS_MD_BLOCK_MAP = 1,
   INCFS_MD_FILE_ATTR = 2,
   INCFS_MD_SIGNATURE = 3,
   INCFS_MD_STATUS = 4,
   INCFS_MD_VERITY_SIGNATURE = 5,
};
 
enum incfs_file_header_flags {
   INCFS_FILE_MAPPED = 1 << 1,
};
 
/* Header included at the beginning of all metadata records on the disk. */
struct incfs_md_header {
   __u8 h_md_entry_type;
 
   /*
    * Size of the metadata record.
    * (e.g. inode, dir entry etc) not just this struct.
    */
   __le16 h_record_size;
 
   /*
    * Was: CRC32 of the metadata record.
    * (e.g. inode, dir entry etc) not just this struct.
    */
   __le32 h_unused1;
 
   /* Offset of the next metadata entry if any */
   __le64 h_next_md_offset;
 
   /* Was: Offset of the previous metadata entry if any */
   __le64 h_unused2;
 
} __packed;
 
/* Backing file header */
struct incfs_file_header {
   /* Magic number: INCFS_MAGIC_NUMBER */
   __le64 fh_magic;
 
   /* Format version: INCFS_FORMAT_CURRENT_VER */
   __le64 fh_version;
 
   /* sizeof(incfs_file_header) */
   __le16 fh_header_size;
 
   /* INCFS_DATA_FILE_BLOCK_SIZE */
   __le16 fh_data_block_size;
 
   /* File flags, from incfs_file_header_flags */
   __le32 fh_flags;
 
   union {
       /* Standard incfs file */
       struct {
           /* Offset of the first metadata record */
           __le64 fh_first_md_offset;
 
           /* Full size of the file's content */
           __le64 fh_file_size;
 
           /* File uuid */
           incfs_uuid_t fh_uuid;
       };
 
       /* Mapped file - INCFS_FILE_MAPPED set in fh_flags */
       struct {
           /* Offset in original file */
           __le64 fh_original_offset;
 
           /* Full size of the file's content */
           __le64 fh_mapped_file_size;
 
           /* Original file's uuid */
           incfs_uuid_t fh_original_uuid;
       };
   };
} __packed;
 
enum incfs_block_map_entry_flags {
   INCFS_BLOCK_COMPRESSED_LZ4 = 1,
   INCFS_BLOCK_COMPRESSED_ZSTD = 2,
 
   /* Reserve 3 bits for compression alg */
   INCFS_BLOCK_COMPRESSED_MASK = 7,
};
 
/* Block map entry pointing to an actual location of the data block. */
struct incfs_blockmap_entry {
   /* Offset of the actual data block. Lower 32 bits */
   __le32 me_data_offset_lo;
 
   /* Offset of the actual data block. Higher 16 bits */
   __le16 me_data_offset_hi;
 
   /* How many bytes the data actually occupies in the backing file */
   __le16 me_data_size;
 
   /* Block flags from incfs_block_map_entry_flags */
   __le16 me_flags;
} __packed;
 
/* Metadata record for locations of file blocks. Type = INCFS_MD_BLOCK_MAP */
struct incfs_blockmap {
   struct incfs_md_header m_header;
 
   /* Base offset of the array of incfs_blockmap_entry */
   __le64 m_base_offset;
 
   /* Size of the map entry array in blocks */
   __le32 m_block_count;
} __packed;
 
/*
 * Metadata record for file signature. Type = INCFS_MD_SIGNATURE
 *
 * The signature stored here is the APK V4 signature data blob. See the
 * definition of incfs_new_file_args::signature_info for an explanation of this
 * blob. Specifically, it contains the root hash, but it does *not* contain
 * anything that the kernel treats as a signature.
 *
 * When FS_IOC_ENABLE_VERITY is called on a file without this record, an APK V4
 * signature blob and a hash tree are added to the file, and then this metadata
 * record is created to record their locations.
 */
struct incfs_file_signature {
   struct incfs_md_header sg_header;
 
   __le32 sg_sig_size; /* The size of the signature. */
 
   __le64 sg_sig_offset; /* Signature's offset in the backing file */
 
   __le32 sg_hash_tree_size; /* The size of the hash tree. */
 
   __le64 sg_hash_tree_offset; /* Hash tree offset in the backing file */
} __packed;
 
/* In memory version of above */
struct incfs_df_signature {
   u32 sig_size;
   u64 sig_offset;
   u32 hash_size;
   u64 hash_offset;
};
 
struct incfs_status {
   struct incfs_md_header is_header;
 
   __le32 is_data_blocks_written; /* Number of data blocks written */
 
   __le32 is_hash_blocks_written; /* Number of hash blocks written */
 
   __le32 is_dummy[6]; /* Spare fields */
} __packed;
 
/*
 * Metadata record for verity signature. Type = INCFS_MD_VERITY_SIGNATURE
 *
 * This record will only exist for verity-enabled files with signatures. Verity
 * enabled files without signatures do not have this record. This signature is
 * checked by fs-verity identically to any other fs-verity signature.
 */
struct incfs_file_verity_signature {
   struct incfs_md_header vs_header;
 
    /* The size of the signature */
   __le32 vs_size;
 
    /* Signature's offset in the backing file */
   __le64 vs_offset;
} __packed;
 
/* In memory version of above */
struct incfs_df_verity_signature {
   u32 size;
   u64 offset;
};
 
/* State of the backing file. */
struct backing_file_context {
   /* Protects writes to bc_file */
   struct mutex bc_mutex;
 
   /* File object to read data from */
   struct file *bc_file;
 
   /*
    * Offset of the last known metadata record in the backing file.
    * 0 means there are no metadata records.
    */
   loff_t bc_last_md_record_offset;
 
   /*
    * Credentials to set before reads/writes
    * Note that this is a pointer to the mount_info mi_owner field so
    * there is no need to get/put the creds
    */
   const struct cred *bc_cred;
};
 
struct metadata_handler {
   loff_t md_record_offset;
   loff_t md_prev_record_offset;
   void *context;
 
   union {
       struct incfs_md_header md_header;
       struct incfs_blockmap blockmap;
       struct incfs_file_signature signature;
       struct incfs_status status;
       struct incfs_file_verity_signature verity_signature;
   } md_buffer;
 
   int (*handle_blockmap)(struct incfs_blockmap *bm,
                  struct metadata_handler *handler);
   int (*handle_signature)(struct incfs_file_signature *sig,
                struct metadata_handler *handler);
   int (*handle_status)(struct incfs_status *sig,
                struct metadata_handler *handler);
   int (*handle_verity_signature)(struct incfs_file_verity_signature *s,
                   struct metadata_handler *handler);
};
#define INCFS_MAX_METADATA_RECORD_SIZE \
   sizeof_field(struct metadata_handler, md_buffer)
 
/* Backing file context management */
struct mount_info;
struct backing_file_context *incfs_alloc_bfc(struct mount_info *mi,
                        struct file *backing_file);
 
void incfs_free_bfc(struct backing_file_context *bfc);
 
/* Writing stuff */
int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
                    u32 block_count);
 
int incfs_write_fh_to_backing_file(struct backing_file_context *bfc,
                  incfs_uuid_t *uuid, u64 file_size);
 
int incfs_write_mapping_fh_to_backing_file(struct backing_file_context *bfc,
               incfs_uuid_t *uuid, u64 file_size, u64 offset);
 
int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,
                      struct mem_range block,
                      int block_index, loff_t bm_base_off,
                      u16 flags);
 
int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
                      struct mem_range block,
                      int block_index,
                      loff_t hash_area_off,
                      loff_t bm_base_off,
                      loff_t file_size);
 
int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
               struct mem_range sig, u32 tree_size,
               loff_t *tree_offset, loff_t *sig_offset);
 
int incfs_write_status_to_backing_file(struct backing_file_context *bfc,
                      loff_t status_offset,
                      u32 data_blocks_written,
                      u32 hash_blocks_written);
int incfs_write_verity_signature_to_backing_file(
       struct backing_file_context *bfc, struct mem_range signature,
       loff_t *offset);
 
/* Reading stuff */
int incfs_read_file_header(struct backing_file_context *bfc,
              loff_t *first_md_off, incfs_uuid_t *uuid,
              u64 *file_size, u32 *flags);
 
int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
                 loff_t bm_base_off,
                 struct incfs_blockmap_entry *bm_entry);
 
int incfs_read_blockmap_entries(struct backing_file_context *bfc,
       struct incfs_blockmap_entry *entries,
       int start_index, int blocks_number,
       loff_t bm_base_off);
 
int incfs_read_next_metadata_record(struct backing_file_context *bfc,
                   struct metadata_handler *handler);
 
ssize_t incfs_kread(struct backing_file_context *bfc, void *buf, size_t size,
           loff_t pos);
ssize_t incfs_kwrite(struct backing_file_context *bfc, const void *buf,
            size_t size, loff_t pos);
 
#endif /* _INCFS_FORMAT_H */