xref: /aosp_15_r20/external/f2fs-tools/fsck/xattr.c (revision 59bfda1f02d633cd6b8b69f31eee485d40f6eef6)
1 /**
2  * xattr.c
3  *
4  * Many parts of codes are copied from Linux kernel/fs/f2fs.
5  *
6  * Copyright (C) 2015 Huawei Ltd.
7  * Witten by:
8  *   Hou Pengyang <[email protected]>
9  *   Liu Shuoran <[email protected]>
10  *   Jaegeuk Kim <[email protected]>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2 as
14  * published by the Free Software Foundation.
15  */
16 #include "fsck.h"
17 #include "node.h"
18 #include "xattr.h"
19 
read_all_xattrs(struct f2fs_sb_info * sbi,struct f2fs_node * inode,bool sanity_check)20 void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
21 			bool sanity_check)
22 {
23 	struct f2fs_xattr_header *header;
24 	void *txattr_addr;
25 	u64 inline_size = inline_xattr_size(&inode->i);
26 	nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid);
27 
28 	if (c.func == FSCK && xnid && sanity_check) {
29 		if (fsck_sanity_check_nid(sbi, xnid, F2FS_FT_XATTR, TYPE_XATTR))
30 			return NULL;
31 	}
32 
33 	txattr_addr = calloc(inline_size + F2FS_BLKSIZE, 1);
34 	ASSERT(txattr_addr);
35 
36 	if (inline_size)
37 		memcpy(txattr_addr, inline_xattr_addr(&inode->i), inline_size);
38 
39 	/* Read from xattr node block. */
40 	if (xnid) {
41 		struct node_info ni;
42 		int ret;
43 
44 		get_node_info(sbi, xnid, &ni);
45 		ret = dev_read_block(txattr_addr + inline_size, ni.blk_addr);
46 		ASSERT(ret >= 0);
47 		memset(txattr_addr + inline_size + F2FS_BLKSIZE -
48 				sizeof(struct node_footer), 0,
49 				sizeof(struct node_footer));
50 	}
51 
52 	header = XATTR_HDR(txattr_addr);
53 
54 	/* Never been allocated xattrs */
55 	if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) {
56 		header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
57 		header->h_refcount = cpu_to_le32(1);
58 	}
59 	return txattr_addr;
60 }
61 
__find_xattr(void * base_addr,void * last_base_addr,int index,size_t len,const char * name)62 static struct f2fs_xattr_entry *__find_xattr(void *base_addr,
63 				void *last_base_addr, int index,
64 				size_t len, const char *name)
65 {
66 	struct f2fs_xattr_entry *entry;
67 
68 	list_for_each_xattr(entry, base_addr) {
69 		if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
70 			(void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) {
71 			MSG(0, "xattr entry crosses the end of xattr space\n");
72 			return NULL;
73 		}
74 
75 		if (entry->e_name_index != index)
76 			continue;
77 		if (entry->e_name_len != len)
78 			continue;
79 		if (!memcmp(entry->e_name, name, len))
80 			break;
81 	}
82 	return entry;
83 }
84 
write_all_xattrs(struct f2fs_sb_info * sbi,struct f2fs_node * inode,__u32 hsize,void * txattr_addr)85 void write_all_xattrs(struct f2fs_sb_info *sbi,
86 		struct f2fs_node *inode, __u32 hsize, void *txattr_addr)
87 {
88 	void *xattr_addr;
89 	struct dnode_of_data dn;
90 	struct node_info ni;
91 	struct f2fs_node *xattr_node;
92 	nid_t new_nid = 0;
93 	block_t blkaddr;
94 	nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid);
95 	u64 inline_size = inline_xattr_size(&inode->i);
96 	int ret;
97 	bool xattrblk_alloced = false;
98 	struct seg_entry *se;
99 
100 	memcpy(inline_xattr_addr(&inode->i), txattr_addr, inline_size);
101 
102 	if (hsize <= inline_size)
103 		return;
104 
105 	if (!xnid) {
106 		f2fs_alloc_nid(sbi, &new_nid);
107 
108 		set_new_dnode(&dn, inode, NULL, new_nid);
109 		/* NAT entry would be updated by new_node_page. */
110 		blkaddr = new_node_block(sbi, &dn, XATTR_NODE_OFFSET);
111 		ASSERT(dn.node_blk);
112 		xattr_node = dn.node_blk;
113 		inode->i.i_xattr_nid = cpu_to_le32(new_nid);
114 		xattrblk_alloced = true;
115 	} else {
116 		set_new_dnode(&dn, inode, NULL, xnid);
117 		get_node_info(sbi, xnid, &ni);
118 		blkaddr = ni.blk_addr;
119 		xattr_node = calloc(F2FS_BLKSIZE, 1);
120 		ASSERT(xattr_node);
121 		ret = dev_read_block(xattr_node, ni.blk_addr);
122 		if (ret < 0)
123 			goto free_xattr_node;
124 	}
125 
126 	/* write to xattr node block */
127 	xattr_addr = (void *)xattr_node;
128 	memcpy(xattr_addr, txattr_addr + inline_size,
129 			F2FS_BLKSIZE - sizeof(struct node_footer));
130 	se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr));
131 	ret = xattrblk_alloced ? dev_write_block(xattr_node, blkaddr,
132 					f2fs_io_type_to_rw_hint(se->type)) :
133 		update_block(sbi, xattr_node, &blkaddr, NULL);
134 
135 free_xattr_node:
136 	free(xattr_node);
137 	ASSERT(ret >= 0);
138 }
139 
f2fs_setxattr(struct f2fs_sb_info * sbi,nid_t ino,int index,const char * name,const void * value,size_t size,int flags)140 int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *name,
141 		const void *value, size_t size, int flags)
142 {
143 	struct f2fs_node *inode;
144 	void *base_addr;
145 	void *last_base_addr;
146 	struct f2fs_xattr_entry *here, *last;
147 	struct node_info ni;
148 	int error = 0;
149 	int len;
150 	int found, newsize;
151 	__u32 new_hsize;
152 	int ret;
153 
154 	if (name == NULL)
155 		return -EINVAL;
156 
157 	if (value == NULL)
158 		return -EINVAL;
159 
160 	len = strlen(name);
161 
162 	if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN)
163 		return -ERANGE;
164 
165 	if (ino < 3)
166 		return -EINVAL;
167 
168 	/* Now We just support selinux */
169 	ASSERT(index == F2FS_XATTR_INDEX_SECURITY);
170 
171 	get_node_info(sbi, ino, &ni);
172 	inode = calloc(F2FS_BLKSIZE, 1);
173 	ASSERT(inode);
174 	ret = dev_read_block(inode, ni.blk_addr);
175 	ASSERT(ret >= 0);
176 
177 	base_addr = read_all_xattrs(sbi, inode, true);
178 	ASSERT(base_addr);
179 
180 	last_base_addr = (void *)base_addr + XATTR_SIZE(&inode->i);
181 
182 	here = __find_xattr(base_addr, last_base_addr, index, len, name);
183 	if (!here) {
184 		MSG(0, "Need to run fsck due to corrupted xattr.\n");
185 		error = -EINVAL;
186 		goto exit;
187 	}
188 
189 	found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
190 
191 	if ((flags & XATTR_REPLACE) && !found) {
192 		error = -ENODATA;
193 		goto exit;
194 	} else if ((flags & XATTR_CREATE) && found) {
195 		error = -EEXIST;
196 		goto exit;
197 	}
198 
199 	last = here;
200 	while (!IS_XATTR_LAST_ENTRY(last))
201 		last = XATTR_NEXT_ENTRY(last);
202 
203 	newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size);
204 
205 	/* 1. Check space */
206 	if (value) {
207 		int free;
208 		/*
209 		 * If value is NULL, it is remove operation.
210 		 * In case of update operation, we calculate free.
211 		 */
212 		free = MIN_OFFSET - ((char *)last - (char *)base_addr);
213 		if (found)
214 			free = free + ENTRY_SIZE(here);
215 		if (free < newsize) {
216 			error = -ENOSPC;
217 			goto exit;
218 		}
219 	}
220 
221 	/* 2. Remove old entry */
222 	if (found) {
223 		/*
224 		 * If entry if sound, remove old entry.
225 		 * If not found, remove operation is not needed
226 		 */
227 		struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here);
228 		int oldsize = ENTRY_SIZE(here);
229 
230 		memmove(here, next, (char *)last - (char *)next);
231 		last = (struct f2fs_xattr_entry *)((char *)last - oldsize);
232 		memset(last, 0, oldsize);
233 
234 	}
235 
236 	new_hsize = (char *)last - (char *)base_addr;
237 
238 	/* 3. Write new entry */
239 	if (value) {
240 		char *pval;
241 		/*
242 		 * Before we come here, old entry is removed.
243 		 * We just write new entry.
244 		 */
245 		memset(last, 0, newsize);
246 		last->e_name_index = index;
247 		last->e_name_len = len;
248 		memcpy(last->e_name, name, len);
249 		pval = last->e_name + len;
250 		memcpy(pval, value, size);
251 		last->e_value_size = cpu_to_le16(size);
252 		new_hsize += newsize;
253 	}
254 
255 	write_all_xattrs(sbi, inode, new_hsize, base_addr);
256 
257 	/* inode need update */
258 	ASSERT(update_inode(sbi, inode, &ni.blk_addr) >= 0);
259 exit:
260 	free(inode);
261 	free(base_addr);
262 	return error;
263 }
264 
inode_set_selinux(struct f2fs_sb_info * sbi,u32 ino,const char * secon)265 int inode_set_selinux(struct f2fs_sb_info *sbi, u32 ino, const char *secon)
266 {
267 	if (!secon)
268 		return 0;
269 
270 	return f2fs_setxattr(sbi, ino, F2FS_XATTR_INDEX_SECURITY,
271 			XATTR_SELINUX_SUFFIX, secon, strlen(secon), 1);
272 }
273