This repository has been archived by the owner on Jun 29, 2021. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathextents.c
147 lines (127 loc) · 4.03 KB
/
extents.c
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
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/apfs/extents.c
*
* Copyright (C) 2018 Ernesto A. Fernández <[email protected]>
*/
#include <linux/buffer_head.h>
#include <linux/slab.h>
#include "apfs.h"
#include "btree.h"
#include "extents.h"
#include "inode.h"
#include "key.h"
#include "message.h"
#include "node.h"
#include "super.h"
/**
* apfs_extent_from_query - Read the extent found by a successful query
* @query: the query that found the record
* @extent: Return parameter. The extent found.
*
* Reads the extent record into @extent and performs some basic sanity checks
* as a protection against crafted filesystems. Returns 0 on success or
* -EFSCORRUPTED otherwise.
*/
int apfs_extent_from_query(struct apfs_query *query,
struct apfs_file_extent *extent)
{
struct super_block *sb = query->node->object.sb;
struct apfs_file_extent_val *ext;
struct apfs_file_extent_key *ext_key;
char *raw = query->node->object.bh->b_data;
u64 ext_len;
if (query->len != sizeof(*ext) || query->key_len != sizeof(*ext_key))
return -EFSCORRUPTED;
ext = (struct apfs_file_extent_val *)(raw + query->off);
ext_key = (struct apfs_file_extent_key *)(raw + query->key_off);
ext_len = le64_to_cpu(ext->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK;
/* Extent length must be a multiple of the block size */
if (ext_len & (sb->s_blocksize - 1))
return -EFSCORRUPTED;
extent->logical_addr = le64_to_cpu(ext_key->logical_addr);
extent->phys_block_num = le64_to_cpu(ext->phys_block_num);
extent->len = ext_len;
return 0;
}
/**
* apfs_extent_read - Read the extent record that covers a block
* @inode: inode that owns the record
* @iblock: logical number of the wanted block
* @extent: Return parameter. The extent found.
*
* Finds and caches the extent record. On success, returns a pointer to the
* cache record; on failure, returns an error code.
*/
static int apfs_extent_read(struct inode *inode, sector_t iblock,
struct apfs_file_extent *extent)
{
struct super_block *sb = inode->i_sb;
struct apfs_sb_info *sbi = APFS_SB(sb);
struct apfs_inode_info *ai = APFS_I(inode);
struct apfs_key key;
struct apfs_query *query;
struct apfs_file_extent *cache = &ai->i_cached_extent;
u64 iaddr = iblock << inode->i_blkbits;
int ret = 0;
spin_lock(&ai->i_extent_lock);
if (iaddr >= cache->logical_addr &&
iaddr < cache->logical_addr + cache->len) {
*extent = *cache;
spin_unlock(&ai->i_extent_lock);
return 0;
}
spin_unlock(&ai->i_extent_lock);
/* We will search for the extent that covers iblock */
apfs_init_file_extent_key(ai->i_extent_id, iaddr, &key);
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
if (!query)
return -ENOMEM;
query->key = &key;
query->flags = APFS_QUERY_CAT;
ret = apfs_btree_query(sb, &query);
if (ret)
goto done;
ret = apfs_extent_from_query(query, extent);
if (ret) {
apfs_alert(sb, "bad extent record for inode 0x%llx",
(unsigned long long) inode->i_ino);
goto done;
}
spin_lock(&ai->i_extent_lock);
*cache = *extent;
spin_unlock(&ai->i_extent_lock);
done:
apfs_free_query(sb, query);
return ret;
}
int apfs_get_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
struct super_block *sb = inode->i_sb;
struct apfs_file_extent ext;
u64 blk_off, bno, map_len;
int ret;
ret = apfs_extent_read(inode, iblock, &ext);
if (ret)
return ret;
/* Find the block offset of iblock within the extent */
blk_off = iblock - (ext.logical_addr >> inode->i_blkbits);
/* Make sure we don't read past the extent boundaries */
map_len = ext.len - (blk_off << inode->i_blkbits);
if (bh_result->b_size > map_len)
bh_result->b_size = map_len;
/*
* Save the requested mapping length as map_bh() replaces it with
* the filesystem block size
*/
map_len = bh_result->b_size;
/* Extents representing holes have block number 0 */
if (ext.phys_block_num != 0) {
/* Find the block number of iblock within the disk */
bno = ext.phys_block_num + blk_off;
map_bh(bh_result, sb, bno);
}
bh_result->b_size = map_len;
return 0;
}