1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2018 Google LLC
4 */
5 #include <linux/fs.h>
6 #include <linux/file.h>
7 #include <linux/types.h>
8 #include <linux/mutex.h>
9 #include <linux/mm.h>
10 #include <linux/falloc.h>
11 #include <linux/slab.h>
12 #include <linux/crc32.h>
13 #include <linux/kernel.h>
14
15 #include "format.h"
16 #include "data_mgmt.h"
17
incfs_alloc_bfc(struct mount_info * mi,struct file * backing_file)18 struct backing_file_context *incfs_alloc_bfc(struct mount_info *mi,
19 struct file *backing_file)
20 {
21 struct backing_file_context *result = NULL;
22
23 result = kzalloc(sizeof(*result), GFP_NOFS);
24 if (!result)
25 return ERR_PTR(-ENOMEM);
26
27 result->bc_file = get_file(backing_file);
28 result->bc_cred = mi->mi_owner;
29 mutex_init(&result->bc_mutex);
30 return result;
31 }
32
incfs_free_bfc(struct backing_file_context * bfc)33 void incfs_free_bfc(struct backing_file_context *bfc)
34 {
35 if (!bfc)
36 return;
37
38 if (bfc->bc_file)
39 fput(bfc->bc_file);
40
41 mutex_destroy(&bfc->bc_mutex);
42 kfree(bfc);
43 }
44
incfs_get_end_offset(struct file * f)45 static loff_t incfs_get_end_offset(struct file *f)
46 {
47 /*
48 * This function assumes that file size and the end-offset
49 * are the same. This is not always true.
50 */
51 return i_size_read(file_inode(f));
52 }
53
54 /*
55 * Truncate the tail of the file to the given length.
56 * Used to rollback partially successful multistep writes.
57 */
truncate_backing_file(struct backing_file_context * bfc,loff_t new_end)58 static int truncate_backing_file(struct backing_file_context *bfc,
59 loff_t new_end)
60 {
61 struct inode *inode = NULL;
62 struct dentry *dentry = NULL;
63 loff_t old_end = 0;
64 struct iattr attr;
65 int result = 0;
66
67 if (!bfc)
68 return -EFAULT;
69
70 LOCK_REQUIRED(bfc->bc_mutex);
71
72 if (!bfc->bc_file)
73 return -EFAULT;
74
75 old_end = incfs_get_end_offset(bfc->bc_file);
76 if (old_end == new_end)
77 return 0;
78 if (old_end < new_end)
79 return -EINVAL;
80
81 inode = bfc->bc_file->f_inode;
82 dentry = bfc->bc_file->f_path.dentry;
83
84 attr.ia_size = new_end;
85 attr.ia_valid = ATTR_SIZE;
86
87 inode_lock(inode);
88 result = notify_change(&init_user_ns, dentry, &attr, NULL);
89 inode_unlock(inode);
90
91 return result;
92 }
93
write_to_bf(struct backing_file_context * bfc,const void * buf,size_t count,loff_t pos)94 static int write_to_bf(struct backing_file_context *bfc, const void *buf,
95 size_t count, loff_t pos)
96 {
97 ssize_t res = incfs_kwrite(bfc, buf, count, pos);
98
99 if (res < 0)
100 return res;
101 if (res != count)
102 return -EIO;
103 return 0;
104 }
105
append_zeros_no_fallocate(struct backing_file_context * bfc,size_t file_size,size_t len)106 static int append_zeros_no_fallocate(struct backing_file_context *bfc,
107 size_t file_size, size_t len)
108 {
109 u8 buffer[256] = {};
110 size_t i;
111
112 for (i = 0; i < len; i += sizeof(buffer)) {
113 int to_write = len - i > sizeof(buffer)
114 ? sizeof(buffer) : len - i;
115 int err = write_to_bf(bfc, buffer, to_write, file_size + i);
116
117 if (err)
118 return err;
119 }
120
121 return 0;
122 }
123
124 /* Append a given number of zero bytes to the end of the backing file. */
append_zeros(struct backing_file_context * bfc,size_t len)125 static int append_zeros(struct backing_file_context *bfc, size_t len)
126 {
127 loff_t file_size = 0;
128 loff_t new_last_byte_offset = 0;
129 int result;
130
131 if (!bfc)
132 return -EFAULT;
133
134 if (len == 0)
135 return 0;
136
137 LOCK_REQUIRED(bfc->bc_mutex);
138
139 /*
140 * Allocate only one byte at the new desired end of the file.
141 * It will increase file size and create a zeroed area of
142 * a given size.
143 */
144 file_size = incfs_get_end_offset(bfc->bc_file);
145 new_last_byte_offset = file_size + len - 1;
146 result = vfs_fallocate(bfc->bc_file, 0, new_last_byte_offset, 1);
147 if (result != -EOPNOTSUPP)
148 return result;
149
150 return append_zeros_no_fallocate(bfc, file_size, len);
151 }
152
153 /*
154 * Append a given metadata record to the backing file and update a previous
155 * record to add the new record the the metadata list.
156 */
append_md_to_backing_file(struct backing_file_context * bfc,struct incfs_md_header * record)157 static int append_md_to_backing_file(struct backing_file_context *bfc,
158 struct incfs_md_header *record)
159 {
160 int result = 0;
161 loff_t record_offset;
162 loff_t file_pos;
163 __le64 new_md_offset;
164 size_t record_size;
165
166 if (!bfc || !record)
167 return -EFAULT;
168
169 if (bfc->bc_last_md_record_offset < 0)
170 return -EINVAL;
171
172 LOCK_REQUIRED(bfc->bc_mutex);
173
174 record_size = le16_to_cpu(record->h_record_size);
175 file_pos = incfs_get_end_offset(bfc->bc_file);
176 record->h_next_md_offset = 0;
177
178 /* Write the metadata record to the end of the backing file */
179 record_offset = file_pos;
180 new_md_offset = cpu_to_le64(record_offset);
181 result = write_to_bf(bfc, record, record_size, file_pos);
182 if (result)
183 return result;
184
185 /* Update next metadata offset in a previous record or a superblock. */
186 if (bfc->bc_last_md_record_offset) {
187 /*
188 * Find a place in the previous md record where new record's
189 * offset needs to be saved.
190 */
191 file_pos = bfc->bc_last_md_record_offset +
192 offsetof(struct incfs_md_header, h_next_md_offset);
193 } else {
194 /*
195 * No metadata yet, file a place to update in the
196 * file_header.
197 */
198 file_pos = offsetof(struct incfs_file_header,
199 fh_first_md_offset);
200 }
201 result = write_to_bf(bfc, &new_md_offset, sizeof(new_md_offset),
202 file_pos);
203 if (result)
204 return result;
205
206 bfc->bc_last_md_record_offset = record_offset;
207 return result;
208 }
209
210 /*
211 * Reserve 0-filled space for the blockmap body, and append
212 * incfs_blockmap metadata record pointing to it.
213 */
incfs_write_blockmap_to_backing_file(struct backing_file_context * bfc,u32 block_count)214 int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
215 u32 block_count)
216 {
217 struct incfs_blockmap blockmap = {};
218 int result = 0;
219 loff_t file_end = 0;
220 size_t map_size = block_count * sizeof(struct incfs_blockmap_entry);
221
222 if (!bfc)
223 return -EFAULT;
224
225 blockmap.m_header.h_md_entry_type = INCFS_MD_BLOCK_MAP;
226 blockmap.m_header.h_record_size = cpu_to_le16(sizeof(blockmap));
227 blockmap.m_header.h_next_md_offset = cpu_to_le64(0);
228 blockmap.m_block_count = cpu_to_le32(block_count);
229
230 LOCK_REQUIRED(bfc->bc_mutex);
231
232 /* Reserve 0-filled space for the blockmap body in the backing file. */
233 file_end = incfs_get_end_offset(bfc->bc_file);
234 result = append_zeros(bfc, map_size);
235 if (result)
236 return result;
237
238 /* Write blockmap metadata record pointing to the body written above. */
239 blockmap.m_base_offset = cpu_to_le64(file_end);
240 result = append_md_to_backing_file(bfc, &blockmap.m_header);
241 if (result)
242 /* Error, rollback file changes */
243 truncate_backing_file(bfc, file_end);
244
245 return result;
246 }
247
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)248 int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
249 struct mem_range sig, u32 tree_size,
250 loff_t *tree_offset, loff_t *sig_offset)
251 {
252 struct incfs_file_signature sg = {};
253 int result = 0;
254 loff_t rollback_pos = 0;
255 loff_t tree_area_pos = 0;
256 size_t alignment = 0;
257
258 if (!bfc)
259 return -EFAULT;
260
261 LOCK_REQUIRED(bfc->bc_mutex);
262
263 rollback_pos = incfs_get_end_offset(bfc->bc_file);
264
265 sg.sg_header.h_md_entry_type = INCFS_MD_SIGNATURE;
266 sg.sg_header.h_record_size = cpu_to_le16(sizeof(sg));
267 sg.sg_header.h_next_md_offset = cpu_to_le64(0);
268 if (sig.data != NULL && sig.len > 0) {
269 sg.sg_sig_size = cpu_to_le32(sig.len);
270 sg.sg_sig_offset = cpu_to_le64(rollback_pos);
271
272 result = write_to_bf(bfc, sig.data, sig.len, rollback_pos);
273 if (result)
274 goto err;
275 }
276
277 tree_area_pos = incfs_get_end_offset(bfc->bc_file);
278 if (tree_size > 0) {
279 if (tree_size > 5 * INCFS_DATA_FILE_BLOCK_SIZE) {
280 /*
281 * If hash tree is big enough, it makes sense to
282 * align in the backing file for faster access.
283 */
284 loff_t offset = round_up(tree_area_pos, PAGE_SIZE);
285
286 alignment = offset - tree_area_pos;
287 tree_area_pos = offset;
288 }
289
290 /*
291 * If root hash is not the only hash in the tree.
292 * reserve 0-filled space for the tree.
293 */
294 result = append_zeros(bfc, tree_size + alignment);
295 if (result)
296 goto err;
297
298 sg.sg_hash_tree_size = cpu_to_le32(tree_size);
299 sg.sg_hash_tree_offset = cpu_to_le64(tree_area_pos);
300 }
301
302 /* Write a hash tree metadata record pointing to the hash tree above. */
303 result = append_md_to_backing_file(bfc, &sg.sg_header);
304 err:
305 if (result)
306 /* Error, rollback file changes */
307 truncate_backing_file(bfc, rollback_pos);
308 else {
309 if (tree_offset)
310 *tree_offset = tree_area_pos;
311 if (sig_offset)
312 *sig_offset = rollback_pos;
313 }
314
315 return result;
316 }
317
write_new_status_to_backing_file(struct backing_file_context * bfc,u32 data_blocks_written,u32 hash_blocks_written)318 static int write_new_status_to_backing_file(struct backing_file_context *bfc,
319 u32 data_blocks_written,
320 u32 hash_blocks_written)
321 {
322 int result;
323 loff_t rollback_pos;
324 struct incfs_status is = {
325 .is_header = {
326 .h_md_entry_type = INCFS_MD_STATUS,
327 .h_record_size = cpu_to_le16(sizeof(is)),
328 },
329 .is_data_blocks_written = cpu_to_le32(data_blocks_written),
330 .is_hash_blocks_written = cpu_to_le32(hash_blocks_written),
331 };
332
333 LOCK_REQUIRED(bfc->bc_mutex);
334 rollback_pos = incfs_get_end_offset(bfc->bc_file);
335 result = append_md_to_backing_file(bfc, &is.is_header);
336 if (result)
337 truncate_backing_file(bfc, rollback_pos);
338
339 return result;
340 }
341
incfs_write_status_to_backing_file(struct backing_file_context * bfc,loff_t status_offset,u32 data_blocks_written,u32 hash_blocks_written)342 int incfs_write_status_to_backing_file(struct backing_file_context *bfc,
343 loff_t status_offset,
344 u32 data_blocks_written,
345 u32 hash_blocks_written)
346 {
347 struct incfs_status is;
348 int result;
349
350 if (!bfc)
351 return -EFAULT;
352
353 if (status_offset == 0)
354 return write_new_status_to_backing_file(bfc,
355 data_blocks_written, hash_blocks_written);
356
357 result = incfs_kread(bfc, &is, sizeof(is), status_offset);
358 if (result != sizeof(is))
359 return -EIO;
360
361 is.is_data_blocks_written = cpu_to_le32(data_blocks_written);
362 is.is_hash_blocks_written = cpu_to_le32(hash_blocks_written);
363 result = incfs_kwrite(bfc, &is, sizeof(is), status_offset);
364 if (result != sizeof(is))
365 return -EIO;
366
367 return 0;
368 }
369
incfs_write_verity_signature_to_backing_file(struct backing_file_context * bfc,struct mem_range signature,loff_t * offset)370 int incfs_write_verity_signature_to_backing_file(
371 struct backing_file_context *bfc, struct mem_range signature,
372 loff_t *offset)
373 {
374 struct incfs_file_verity_signature vs = {};
375 int result;
376 loff_t pos;
377
378 /* No verity signature section is equivalent to an empty section */
379 if (signature.data == NULL || signature.len == 0)
380 return 0;
381
382 pos = incfs_get_end_offset(bfc->bc_file);
383
384 vs = (struct incfs_file_verity_signature) {
385 .vs_header = (struct incfs_md_header) {
386 .h_md_entry_type = INCFS_MD_VERITY_SIGNATURE,
387 .h_record_size = cpu_to_le16(sizeof(vs)),
388 .h_next_md_offset = cpu_to_le64(0),
389 },
390 .vs_size = cpu_to_le32(signature.len),
391 .vs_offset = cpu_to_le64(pos),
392 };
393
394 result = write_to_bf(bfc, signature.data, signature.len, pos);
395 if (result)
396 goto err;
397
398 result = append_md_to_backing_file(bfc, &vs.vs_header);
399 if (result)
400 goto err;
401
402 *offset = pos;
403 err:
404 if (result)
405 /* Error, rollback file changes */
406 truncate_backing_file(bfc, pos);
407 return result;
408 }
409
410 /*
411 * Write a backing file header
412 * It should always be called only on empty file.
413 * fh.fh_first_md_offset is 0 for now, but will be updated
414 * once first metadata record is added.
415 */
incfs_write_fh_to_backing_file(struct backing_file_context * bfc,incfs_uuid_t * uuid,u64 file_size)416 int incfs_write_fh_to_backing_file(struct backing_file_context *bfc,
417 incfs_uuid_t *uuid, u64 file_size)
418 {
419 struct incfs_file_header fh = {};
420 loff_t file_pos = 0;
421
422 if (!bfc)
423 return -EFAULT;
424
425 fh.fh_magic = cpu_to_le64(INCFS_MAGIC_NUMBER);
426 fh.fh_version = cpu_to_le64(INCFS_FORMAT_CURRENT_VER);
427 fh.fh_header_size = cpu_to_le16(sizeof(fh));
428 fh.fh_first_md_offset = cpu_to_le64(0);
429 fh.fh_data_block_size = cpu_to_le16(INCFS_DATA_FILE_BLOCK_SIZE);
430
431 fh.fh_file_size = cpu_to_le64(file_size);
432 fh.fh_uuid = *uuid;
433
434 LOCK_REQUIRED(bfc->bc_mutex);
435
436 file_pos = incfs_get_end_offset(bfc->bc_file);
437 if (file_pos != 0)
438 return -EEXIST;
439
440 return write_to_bf(bfc, &fh, sizeof(fh), file_pos);
441 }
442
443 /*
444 * Write a backing file header for a mapping file
445 * It should always be called only on empty file.
446 */
incfs_write_mapping_fh_to_backing_file(struct backing_file_context * bfc,incfs_uuid_t * uuid,u64 file_size,u64 offset)447 int incfs_write_mapping_fh_to_backing_file(struct backing_file_context *bfc,
448 incfs_uuid_t *uuid, u64 file_size, u64 offset)
449 {
450 struct incfs_file_header fh = {};
451 loff_t file_pos = 0;
452
453 if (!bfc)
454 return -EFAULT;
455
456 fh.fh_magic = cpu_to_le64(INCFS_MAGIC_NUMBER);
457 fh.fh_version = cpu_to_le64(INCFS_FORMAT_CURRENT_VER);
458 fh.fh_header_size = cpu_to_le16(sizeof(fh));
459 fh.fh_original_offset = cpu_to_le64(offset);
460 fh.fh_data_block_size = cpu_to_le16(INCFS_DATA_FILE_BLOCK_SIZE);
461
462 fh.fh_mapped_file_size = cpu_to_le64(file_size);
463 fh.fh_original_uuid = *uuid;
464 fh.fh_flags = cpu_to_le32(INCFS_FILE_MAPPED);
465
466 LOCK_REQUIRED(bfc->bc_mutex);
467
468 file_pos = incfs_get_end_offset(bfc->bc_file);
469 if (file_pos != 0)
470 return -EEXIST;
471
472 return write_to_bf(bfc, &fh, sizeof(fh), file_pos);
473 }
474
475 /* Write a given data block and update file's blockmap to point it. */
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)476 int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,
477 struct mem_range block, int block_index,
478 loff_t bm_base_off, u16 flags)
479 {
480 struct incfs_blockmap_entry bm_entry = {};
481 int result = 0;
482 loff_t data_offset = 0;
483 loff_t bm_entry_off =
484 bm_base_off + sizeof(struct incfs_blockmap_entry) * block_index;
485
486 if (!bfc)
487 return -EFAULT;
488
489 if (block.len >= (1 << 16) || block_index < 0)
490 return -EINVAL;
491
492 LOCK_REQUIRED(bfc->bc_mutex);
493
494 data_offset = incfs_get_end_offset(bfc->bc_file);
495 if (data_offset <= bm_entry_off) {
496 /* Blockmap entry is beyond the file's end. It is not normal. */
497 return -EINVAL;
498 }
499
500 /* Write the block data at the end of the backing file. */
501 result = write_to_bf(bfc, block.data, block.len, data_offset);
502 if (result)
503 return result;
504
505 /* Update the blockmap to point to the newly written data. */
506 bm_entry.me_data_offset_lo = cpu_to_le32((u32)data_offset);
507 bm_entry.me_data_offset_hi = cpu_to_le16((u16)(data_offset >> 32));
508 bm_entry.me_data_size = cpu_to_le16((u16)block.len);
509 bm_entry.me_flags = cpu_to_le16(flags);
510
511 return write_to_bf(bfc, &bm_entry, sizeof(bm_entry),
512 bm_entry_off);
513 }
514
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)515 int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
516 struct mem_range block,
517 int block_index,
518 loff_t hash_area_off,
519 loff_t bm_base_off,
520 loff_t file_size)
521 {
522 struct incfs_blockmap_entry bm_entry = {};
523 int result;
524 loff_t data_offset = 0;
525 loff_t file_end = 0;
526 loff_t bm_entry_off =
527 bm_base_off +
528 sizeof(struct incfs_blockmap_entry) *
529 (block_index + get_blocks_count_for_size(file_size));
530
531 if (!bfc)
532 return -EFAULT;
533
534 LOCK_REQUIRED(bfc->bc_mutex);
535
536 data_offset = hash_area_off + block_index * INCFS_DATA_FILE_BLOCK_SIZE;
537 file_end = incfs_get_end_offset(bfc->bc_file);
538 if (data_offset + block.len > file_end) {
539 /* Block is located beyond the file's end. It is not normal. */
540 return -EINVAL;
541 }
542
543 result = write_to_bf(bfc, block.data, block.len, data_offset);
544 if (result)
545 return result;
546
547 bm_entry.me_data_offset_lo = cpu_to_le32((u32)data_offset);
548 bm_entry.me_data_offset_hi = cpu_to_le16((u16)(data_offset >> 32));
549 bm_entry.me_data_size = cpu_to_le16(INCFS_DATA_FILE_BLOCK_SIZE);
550
551 return write_to_bf(bfc, &bm_entry, sizeof(bm_entry), bm_entry_off);
552 }
553
incfs_read_blockmap_entry(struct backing_file_context * bfc,int block_index,loff_t bm_base_off,struct incfs_blockmap_entry * bm_entry)554 int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
555 loff_t bm_base_off,
556 struct incfs_blockmap_entry *bm_entry)
557 {
558 int error = incfs_read_blockmap_entries(bfc, bm_entry, block_index, 1,
559 bm_base_off);
560
561 if (error < 0)
562 return error;
563
564 if (error == 0)
565 return -EIO;
566
567 if (error != 1)
568 return -EFAULT;
569
570 return 0;
571 }
572
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)573 int incfs_read_blockmap_entries(struct backing_file_context *bfc,
574 struct incfs_blockmap_entry *entries,
575 int start_index, int blocks_number,
576 loff_t bm_base_off)
577 {
578 loff_t bm_entry_off =
579 bm_base_off + sizeof(struct incfs_blockmap_entry) * start_index;
580 const size_t bytes_to_read = sizeof(struct incfs_blockmap_entry)
581 * blocks_number;
582 int result = 0;
583
584 if (!bfc || !entries)
585 return -EFAULT;
586
587 if (start_index < 0 || bm_base_off <= 0)
588 return -ENODATA;
589
590 result = incfs_kread(bfc, entries, bytes_to_read, bm_entry_off);
591 if (result < 0)
592 return result;
593 return result / sizeof(*entries);
594 }
595
incfs_read_file_header(struct backing_file_context * bfc,loff_t * first_md_off,incfs_uuid_t * uuid,u64 * file_size,u32 * flags)596 int incfs_read_file_header(struct backing_file_context *bfc,
597 loff_t *first_md_off, incfs_uuid_t *uuid,
598 u64 *file_size, u32 *flags)
599 {
600 ssize_t bytes_read = 0;
601 struct incfs_file_header fh = {};
602
603 if (!bfc || !first_md_off)
604 return -EFAULT;
605
606 bytes_read = incfs_kread(bfc, &fh, sizeof(fh), 0);
607 if (bytes_read < 0)
608 return bytes_read;
609
610 if (bytes_read < sizeof(fh))
611 return -EBADMSG;
612
613 if (le64_to_cpu(fh.fh_magic) != INCFS_MAGIC_NUMBER)
614 return -EILSEQ;
615
616 if (le64_to_cpu(fh.fh_version) > INCFS_FORMAT_CURRENT_VER)
617 return -EILSEQ;
618
619 if (le16_to_cpu(fh.fh_data_block_size) != INCFS_DATA_FILE_BLOCK_SIZE)
620 return -EILSEQ;
621
622 if (le16_to_cpu(fh.fh_header_size) != sizeof(fh))
623 return -EILSEQ;
624
625 if (first_md_off)
626 *first_md_off = le64_to_cpu(fh.fh_first_md_offset);
627 if (uuid)
628 *uuid = fh.fh_uuid;
629 if (file_size)
630 *file_size = le64_to_cpu(fh.fh_file_size);
631 if (flags)
632 *flags = le32_to_cpu(fh.fh_flags);
633 return 0;
634 }
635
636 /*
637 * Read through metadata records from the backing file one by one
638 * and call provided metadata handlers.
639 */
incfs_read_next_metadata_record(struct backing_file_context * bfc,struct metadata_handler * handler)640 int incfs_read_next_metadata_record(struct backing_file_context *bfc,
641 struct metadata_handler *handler)
642 {
643 const ssize_t max_md_size = INCFS_MAX_METADATA_RECORD_SIZE;
644 ssize_t bytes_read = 0;
645 size_t md_record_size = 0;
646 loff_t next_record = 0;
647 int res = 0;
648 struct incfs_md_header *md_hdr = NULL;
649
650 if (!bfc || !handler)
651 return -EFAULT;
652
653 if (handler->md_record_offset == 0)
654 return -EPERM;
655
656 memset(&handler->md_buffer, 0, max_md_size);
657 bytes_read = incfs_kread(bfc, &handler->md_buffer, max_md_size,
658 handler->md_record_offset);
659 if (bytes_read < 0)
660 return bytes_read;
661 if (bytes_read < sizeof(*md_hdr))
662 return -EBADMSG;
663
664 md_hdr = &handler->md_buffer.md_header;
665 next_record = le64_to_cpu(md_hdr->h_next_md_offset);
666 md_record_size = le16_to_cpu(md_hdr->h_record_size);
667
668 if (md_record_size > max_md_size) {
669 pr_warn("incfs: The record is too large. Size: %zu",
670 md_record_size);
671 return -EBADMSG;
672 }
673
674 if (bytes_read < md_record_size) {
675 pr_warn("incfs: The record hasn't been fully read.");
676 return -EBADMSG;
677 }
678
679 if (next_record <= handler->md_record_offset && next_record != 0) {
680 pr_warn("incfs: Next record (%lld) points back in file.",
681 next_record);
682 return -EBADMSG;
683 }
684
685 switch (md_hdr->h_md_entry_type) {
686 case INCFS_MD_NONE:
687 break;
688 case INCFS_MD_BLOCK_MAP:
689 if (handler->handle_blockmap)
690 res = handler->handle_blockmap(
691 &handler->md_buffer.blockmap, handler);
692 break;
693 case INCFS_MD_FILE_ATTR:
694 /*
695 * File attrs no longer supported, ignore section for
696 * compatibility
697 */
698 break;
699 case INCFS_MD_SIGNATURE:
700 if (handler->handle_signature)
701 res = handler->handle_signature(
702 &handler->md_buffer.signature, handler);
703 break;
704 case INCFS_MD_STATUS:
705 if (handler->handle_status)
706 res = handler->handle_status(
707 &handler->md_buffer.status, handler);
708 break;
709 case INCFS_MD_VERITY_SIGNATURE:
710 if (handler->handle_verity_signature)
711 res = handler->handle_verity_signature(
712 &handler->md_buffer.verity_signature, handler);
713 break;
714 default:
715 res = -ENOTSUPP;
716 break;
717 }
718
719 if (!res) {
720 if (next_record == 0) {
721 /*
722 * Zero offset for the next record means that the last
723 * metadata record has just been processed.
724 */
725 bfc->bc_last_md_record_offset =
726 handler->md_record_offset;
727 }
728 handler->md_prev_record_offset = handler->md_record_offset;
729 handler->md_record_offset = next_record;
730 }
731 return res;
732 }
733
incfs_kread(struct backing_file_context * bfc,void * buf,size_t size,loff_t pos)734 ssize_t incfs_kread(struct backing_file_context *bfc, void *buf, size_t size,
735 loff_t pos)
736 {
737 const struct cred *old_cred = override_creds(bfc->bc_cred);
738 int ret = kernel_read(bfc->bc_file, buf, size, &pos);
739
740 revert_creds(old_cred);
741 return ret;
742 }
743
incfs_kwrite(struct backing_file_context * bfc,const void * buf,size_t size,loff_t pos)744 ssize_t incfs_kwrite(struct backing_file_context *bfc, const void *buf,
745 size_t size, loff_t pos)
746 {
747 const struct cred *old_cred = override_creds(bfc->bc_cred);
748 int ret = kernel_write(bfc->bc_file, buf, size, &pos);
749
750 revert_creds(old_cred);
751 return ret;
752 }
753