• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 /*
3  * erofs-utils/lib/blobchunk.c
4  *
5  * Copyright (C) 2021, Alibaba Cloud
6  */
7 #define _GNU_SOURCE
8 #include "erofs/hashmap.h"
9 #include "erofs/blobchunk.h"
10 #include "erofs/block_list.h"
11 #include "erofs/cache.h"
12 #include "erofs/io.h"
13 #include <unistd.h>
14 
15 void erofs_sha256(const unsigned char *in, unsigned long in_size,
16 		  unsigned char out[32]);
17 
18 struct erofs_blobchunk {
19 	struct hashmap_entry ent;
20 	char		sha256[32];
21 	unsigned int	chunksize;
22 	erofs_blk_t	blkaddr;
23 };
24 
25 static struct hashmap blob_hashmap;
26 static FILE *blobfile;
27 static erofs_blk_t remapped_base;
28 static bool multidev;
29 static struct erofs_buffer_head *bh_devt;
30 
erofs_blob_getchunk(int fd,unsigned int chunksize)31 static struct erofs_blobchunk *erofs_blob_getchunk(int fd,
32 		unsigned int chunksize)
33 {
34 	static u8 zeroed[EROFS_BLKSIZ];
35 	u8 *chunkdata, sha256[32];
36 	int ret;
37 	unsigned int hash;
38 	erofs_off_t blkpos;
39 	struct erofs_blobchunk *chunk;
40 
41 	chunkdata = malloc(chunksize);
42 	if (!chunkdata)
43 		return ERR_PTR(-ENOMEM);
44 
45 	ret = read(fd, chunkdata, chunksize);
46 	if (ret < chunksize) {
47 		chunk = ERR_PTR(-EIO);
48 		goto out;
49 	}
50 	erofs_sha256(chunkdata, chunksize, sha256);
51 	hash = memhash(sha256, sizeof(sha256));
52 	chunk = hashmap_get_from_hash(&blob_hashmap, hash, sha256);
53 	if (chunk) {
54 		DBG_BUGON(chunksize != chunk->chunksize);
55 		goto out;
56 	}
57 	chunk = malloc(sizeof(struct erofs_blobchunk));
58 	if (!chunk) {
59 		chunk = ERR_PTR(-ENOMEM);
60 		goto out;
61 	}
62 
63 	chunk->chunksize = chunksize;
64 	blkpos = ftell(blobfile);
65 	DBG_BUGON(erofs_blkoff(blkpos));
66 	chunk->blkaddr = erofs_blknr(blkpos);
67 	memcpy(chunk->sha256, sha256, sizeof(sha256));
68 	hashmap_entry_init(&chunk->ent, hash);
69 	hashmap_add(&blob_hashmap, chunk);
70 
71 	erofs_dbg("Writing chunk (%u bytes) to %u", chunksize, chunk->blkaddr);
72 	ret = fwrite(chunkdata, chunksize, 1, blobfile);
73 	if (ret == 1 && erofs_blkoff(chunksize))
74 		ret = fwrite(zeroed, EROFS_BLKSIZ - erofs_blkoff(chunksize),
75 			     1, blobfile);
76 	if (ret < 1) {
77 		struct hashmap_entry key;
78 
79 		hashmap_entry_init(&key, hash);
80 		hashmap_remove(&blob_hashmap, &key, sha256);
81 		free(chunk);
82 		chunk = ERR_PTR(-ENOSPC);
83 		goto out;
84 	}
85 out:
86 	free(chunkdata);
87 	return chunk;
88 }
89 
erofs_blob_hashmap_cmp(const void * a,const void * b,const void * key)90 static int erofs_blob_hashmap_cmp(const void *a, const void *b,
91 				  const void *key)
92 {
93 	const struct erofs_blobchunk *ec1 =
94 			container_of((struct hashmap_entry *)a,
95 				     struct erofs_blobchunk, ent);
96 	const struct erofs_blobchunk *ec2 =
97 			container_of((struct hashmap_entry *)b,
98 				     struct erofs_blobchunk, ent);
99 
100 	return memcmp(ec1->sha256, key ? key : ec2->sha256,
101 		      sizeof(ec1->sha256));
102 }
103 
erofs_blob_write_chunk_indexes(struct erofs_inode * inode,erofs_off_t off)104 int erofs_blob_write_chunk_indexes(struct erofs_inode *inode,
105 				   erofs_off_t off)
106 {
107 	struct erofs_inode_chunk_index idx = {0};
108 	erofs_blk_t extent_start = EROFS_NULL_ADDR;
109 	erofs_blk_t extent_end, extents_blks;
110 	unsigned int dst, src, unit;
111 	bool first_extent = true;
112 	erofs_blk_t base_blkaddr = 0;
113 
114 	if (multidev) {
115 		idx.device_id = 1;
116 		DBG_BUGON(!(inode->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES));
117 	} else {
118 		base_blkaddr = remapped_base;
119 	}
120 
121 	if (inode->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
122 		unit = sizeof(struct erofs_inode_chunk_index);
123 	else
124 		unit = EROFS_BLOCK_MAP_ENTRY_SIZE;
125 
126 	for (dst = src = 0; dst < inode->extent_isize;
127 	     src += sizeof(void *), dst += unit) {
128 		struct erofs_blobchunk *chunk;
129 
130 		chunk = *(void **)(inode->chunkindexes + src);
131 
132 		idx.blkaddr = base_blkaddr + chunk->blkaddr;
133 		if (extent_start != EROFS_NULL_ADDR &&
134 		    idx.blkaddr == extent_end + 1) {
135 			extent_end = idx.blkaddr;
136 		} else {
137 			if (extent_start != EROFS_NULL_ADDR) {
138 				erofs_droid_blocklist_write_extent(inode,
139 					extent_start,
140 					(extent_end - extent_start) + 1,
141 					first_extent, false);
142 				first_extent = false;
143 			}
144 			extent_start = idx.blkaddr;
145 			extent_end = idx.blkaddr;
146 		}
147 		if (unit == EROFS_BLOCK_MAP_ENTRY_SIZE)
148 			memcpy(inode->chunkindexes + dst, &idx.blkaddr, unit);
149 		else
150 			memcpy(inode->chunkindexes + dst, &idx, sizeof(idx));
151 	}
152 	off = roundup(off, unit);
153 
154 	if (extent_start == EROFS_NULL_ADDR)
155 		extents_blks = 0;
156 	else
157 		extents_blks = (extent_end - extent_start) + 1;
158 	erofs_droid_blocklist_write_extent(inode, extent_start, extents_blks,
159 					   first_extent, true);
160 
161 	return dev_write(inode->chunkindexes, off, inode->extent_isize);
162 }
163 
erofs_blob_write_chunked_file(struct erofs_inode * inode)164 int erofs_blob_write_chunked_file(struct erofs_inode *inode)
165 {
166 	unsigned int chunksize = 1 << cfg.c_chunkbits;
167 	unsigned int count = DIV_ROUND_UP(inode->i_size, chunksize);
168 	struct erofs_inode_chunk_index *idx;
169 	erofs_off_t pos, len;
170 	unsigned int unit;
171 	int fd, ret;
172 
173 	inode->u.chunkformat |= inode->u.chunkbits - LOG_BLOCK_SIZE;
174 	if (multidev)
175 		inode->u.chunkformat |= EROFS_CHUNK_FORMAT_INDEXES;
176 
177 	if (inode->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
178 		unit = sizeof(struct erofs_inode_chunk_index);
179 	else
180 		unit = EROFS_BLOCK_MAP_ENTRY_SIZE;
181 
182 	inode->extent_isize = count * unit;
183 	idx = malloc(count * max(sizeof(*idx), sizeof(void *)));
184 	if (!idx)
185 		return -ENOMEM;
186 	inode->chunkindexes = idx;
187 
188 	fd = open(inode->i_srcpath, O_RDONLY | O_BINARY);
189 	if (fd < 0) {
190 		ret = -errno;
191 		goto err;
192 	}
193 
194 	for (pos = 0; pos < inode->i_size; pos += len) {
195 		struct erofs_blobchunk *chunk;
196 
197 		len = min_t(u64, inode->i_size - pos, chunksize);
198 		chunk = erofs_blob_getchunk(fd, len);
199 		if (IS_ERR(chunk)) {
200 			ret = PTR_ERR(chunk);
201 			close(fd);
202 			goto err;
203 		}
204 		*(void **)idx++ = chunk;
205 	}
206 	inode->datalayout = EROFS_INODE_CHUNK_BASED;
207 	close(fd);
208 	return 0;
209 err:
210 	free(inode->chunkindexes);
211 	inode->chunkindexes = NULL;
212 	return ret;
213 }
214 
erofs_blob_remap(void)215 int erofs_blob_remap(void)
216 {
217 	struct erofs_buffer_head *bh;
218 	ssize_t length;
219 	erofs_off_t pos_in, pos_out;
220 	ssize_t ret;
221 
222 	fflush(blobfile);
223 	length = ftell(blobfile);
224 	if (length < 0)
225 		return -errno;
226 	if (multidev) {
227 		struct erofs_deviceslot dis = {
228 			.blocks = erofs_blknr(length),
229 		};
230 
231 		pos_out = erofs_btell(bh_devt, false);
232 		ret = dev_write(&dis, pos_out, sizeof(dis));
233 		if (ret)
234 			return ret;
235 
236 		bh_devt->op = &erofs_drop_directly_bhops;
237 		erofs_bdrop(bh_devt, false);
238 		return 0;
239 	}
240 	bh = erofs_balloc(DATA, length, 0, 0);
241 	if (IS_ERR(bh))
242 		return PTR_ERR(bh);
243 
244 	erofs_mapbh(bh->block);
245 	pos_out = erofs_btell(bh, false);
246 	pos_in = 0;
247 	remapped_base = erofs_blknr(pos_out);
248 	ret = erofs_copy_file_range(fileno(blobfile), &pos_in,
249 				    erofs_devfd, &pos_out, length);
250 	bh->op = &erofs_drop_directly_bhops;
251 	erofs_bdrop(bh, false);
252 	return ret < length ? -EIO : 0;
253 }
254 
erofs_blob_exit(void)255 void erofs_blob_exit(void)
256 {
257 	if (blobfile)
258 		fclose(blobfile);
259 
260 	hashmap_free(&blob_hashmap, 1);
261 }
262 
erofs_blob_init(const char * blobfile_path)263 int erofs_blob_init(const char *blobfile_path)
264 {
265 	if (!blobfile_path) {
266 #ifdef HAVE_TMPFILE64
267 		blobfile = tmpfile64();
268 #else
269 		blobfile = tmpfile();
270 #endif
271 		multidev = false;
272 	} else {
273 		blobfile = fopen(blobfile_path, "wb");
274 		multidev = true;
275 	}
276 	if (!blobfile)
277 		return -EACCES;
278 
279 	hashmap_init(&blob_hashmap, erofs_blob_hashmap_cmp, 0);
280 	return 0;
281 }
282 
erofs_generate_devtable(void)283 int erofs_generate_devtable(void)
284 {
285 	struct erofs_deviceslot dis;
286 
287 	if (!multidev)
288 		return 0;
289 
290 	bh_devt = erofs_balloc(DEVT, sizeof(dis), 0, 0);
291 	if (IS_ERR(bh_devt))
292 		return PTR_ERR(bh_devt);
293 
294 	dis = (struct erofs_deviceslot) {};
295 	erofs_mapbh(bh_devt->block);
296 	bh_devt->op = &erofs_skip_write_bhops;
297 	sbi.devt_slotoff = erofs_btell(bh_devt, false) / EROFS_DEVT_SLOT_SIZE;
298 	sbi.extra_devices = 1;
299 	erofs_sb_set_device_table();
300 	return 0;
301 }
302