• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 #define _GNU_SOURCE
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <config.h>
8 #if defined(HAVE_SYS_SYSMACROS_H)
9 #include <sys/sysmacros.h>
10 #endif
11 #include "erofs/print.h"
12 #include "erofs/inode.h"
13 #include "erofs/rebuild.h"
14 #include "erofs/io.h"
15 #include "erofs/dir.h"
16 #include "erofs/xattr.h"
17 #include "erofs/blobchunk.h"
18 #include "erofs/internal.h"
19 
20 #ifdef HAVE_LINUX_AUFS_TYPE_H
21 #include <linux/aufs_type.h>
22 #else
23 #define AUFS_WH_PFX		".wh."
24 #define AUFS_DIROPQ_NAME	AUFS_WH_PFX ".opq"
25 #define AUFS_WH_DIROPQ		AUFS_WH_PFX AUFS_DIROPQ_NAME
26 #endif
27 
erofs_rebuild_mkdir(struct erofs_inode * dir,const char * s)28 static struct erofs_dentry *erofs_rebuild_mkdir(struct erofs_inode *dir,
29 						const char *s)
30 {
31 	struct erofs_inode *inode;
32 	struct erofs_dentry *d;
33 
34 	inode = erofs_new_inode();
35 	if (IS_ERR(inode))
36 		return ERR_CAST(inode);
37 
38 	inode->i_mode = S_IFDIR | 0755;
39 	inode->i_parent = dir;
40 	inode->i_uid = getuid();
41 	inode->i_gid = getgid();
42 	inode->i_mtime = inode->sbi->build_time;
43 	inode->i_mtime_nsec = inode->sbi->build_time_nsec;
44 	erofs_init_empty_dir(inode);
45 
46 	d = erofs_d_alloc(dir, s);
47 	if (!IS_ERR(d)) {
48 		d->type = EROFS_FT_DIR;
49 		d->inode = inode;
50 	}
51 	return d;
52 }
53 
erofs_rebuild_get_dentry(struct erofs_inode * pwd,char * path,bool aufs,bool * whout,bool * opq,bool to_head)54 struct erofs_dentry *erofs_rebuild_get_dentry(struct erofs_inode *pwd,
55 		char *path, bool aufs, bool *whout, bool *opq, bool to_head)
56 {
57 	struct erofs_dentry *d = NULL;
58 	unsigned int len = strlen(path);
59 	char *s = path;
60 
61 	*whout = false;
62 	*opq = false;
63 
64 	while (s < path + len) {
65 		char *slash = memchr(s, '/', path + len - s);
66 
67 		if (slash) {
68 			if (s == slash) {
69 				while (*++s == '/');	/* skip '//...' */
70 				continue;
71 			}
72 			*slash = '\0';
73 		}
74 
75 		if (!memcmp(s, ".", 2)) {
76 			/* null */
77 		} else if (!memcmp(s, "..", 3)) {
78 			pwd = pwd->i_parent;
79 		} else {
80 			struct erofs_inode *inode = NULL;
81 
82 			if (aufs && !slash) {
83 				if (!memcmp(s, AUFS_WH_DIROPQ, sizeof(AUFS_WH_DIROPQ))) {
84 					*opq = true;
85 					break;
86 				}
87 				if (!memcmp(s, AUFS_WH_PFX, sizeof(AUFS_WH_PFX) - 1)) {
88 					s += sizeof(AUFS_WH_PFX) - 1;
89 					*whout = true;
90 				}
91 			}
92 
93 			list_for_each_entry(d, &pwd->i_subdirs, d_child) {
94 				if (!strcmp(d->name, s)) {
95 					if (d->type != EROFS_FT_DIR && slash)
96 						return ERR_PTR(-EIO);
97 					inode = d->inode;
98 					break;
99 				}
100 			}
101 
102 			if (inode) {
103 				if (to_head) {
104 					list_del(&d->d_child);
105 					list_add(&d->d_child, &pwd->i_subdirs);
106 				}
107 				pwd = inode;
108 			} else if (!slash) {
109 				d = erofs_d_alloc(pwd, s);
110 				if (IS_ERR(d))
111 					return d;
112 				d->type = EROFS_FT_UNKNOWN;
113 				d->inode = pwd;
114 			} else {
115 				d = erofs_rebuild_mkdir(pwd, s);
116 				if (IS_ERR(d))
117 					return d;
118 				pwd = d->inode;
119 			}
120 		}
121 		if (slash) {
122 			*slash = '/';
123 			s = slash + 1;
124 		} else {
125 			break;
126 		}
127 	}
128 	return d;
129 }
130 
erofs_rebuild_fixup_inode_index(struct erofs_inode * inode)131 static int erofs_rebuild_fixup_inode_index(struct erofs_inode *inode)
132 {
133 	int ret;
134 	unsigned int count, unit, chunkbits, i;
135 	struct erofs_inode_chunk_index *idx;
136 	erofs_off_t chunksize;
137 	erofs_blk_t blkaddr;
138 
139 	/* TODO: fill data map in other layouts */
140 	if (inode->datalayout != EROFS_INODE_CHUNK_BASED &&
141 	    inode->datalayout != EROFS_INODE_FLAT_PLAIN) {
142 		erofs_err("%s: unsupported datalayout %d", inode->i_srcpath, inode->datalayout);
143 		return -EOPNOTSUPP;
144 	}
145 
146 	if (inode->sbi->extra_devices) {
147 		chunkbits = inode->u.chunkbits;
148 		if (chunkbits < sbi.blkszbits) {
149 			erofs_err("%s: chunk size %u is too small to fit the target block size %u",
150 				  inode->i_srcpath, 1U << chunkbits, 1U << sbi.blkszbits);
151 			return -EINVAL;
152 		}
153 	} else {
154 		chunkbits = ilog2(inode->i_size - 1) + 1;
155 		if (chunkbits < sbi.blkszbits)
156 			chunkbits = sbi.blkszbits;
157 		if (chunkbits - sbi.blkszbits > EROFS_CHUNK_FORMAT_BLKBITS_MASK)
158 			chunkbits = EROFS_CHUNK_FORMAT_BLKBITS_MASK + sbi.blkszbits;
159 	}
160 	chunksize = 1ULL << chunkbits;
161 	count = DIV_ROUND_UP(inode->i_size, chunksize);
162 
163 	unit = sizeof(struct erofs_inode_chunk_index);
164 	inode->extent_isize = count * unit;
165 	idx = malloc(max(sizeof(*idx), sizeof(void *)));
166 	if (!idx)
167 		return -ENOMEM;
168 	inode->chunkindexes = idx;
169 
170 	for (i = 0; i < count; i++) {
171 		struct erofs_blobchunk *chunk;
172 		struct erofs_map_blocks map = {
173 			.index = UINT_MAX,
174 		};
175 
176 		map.m_la = i << chunkbits;
177 		ret = erofs_map_blocks(inode, &map, 0);
178 		if (ret)
179 			goto err;
180 
181 		blkaddr = erofs_blknr(&sbi, map.m_pa);
182 		chunk = erofs_get_unhashed_chunk(inode->dev, blkaddr, 0);
183 		if (IS_ERR(chunk)) {
184 			ret = PTR_ERR(chunk);
185 			goto err;
186 		}
187 		*(void **)idx++ = chunk;
188 
189 	}
190 	inode->datalayout = EROFS_INODE_CHUNK_BASED;
191 	inode->u.chunkformat = EROFS_CHUNK_FORMAT_INDEXES;
192 	inode->u.chunkformat |= chunkbits - sbi.blkszbits;
193 	return 0;
194 err:
195 	free(inode->chunkindexes);
196 	inode->chunkindexes = NULL;
197 	return ret;
198 }
199 
erofs_rebuild_fill_inode(struct erofs_inode * inode)200 static int erofs_rebuild_fill_inode(struct erofs_inode *inode)
201 {
202 	switch (inode->i_mode & S_IFMT) {
203 	case S_IFCHR:
204 		if (erofs_inode_is_whiteout(inode))
205 			inode->i_parent->whiteouts = true;
206 		/* fallthrough */
207 	case S_IFBLK:
208 	case S_IFIFO:
209 	case S_IFSOCK:
210 		inode->i_size = 0;
211 		erofs_dbg("\tdev: %d %d", major(inode->u.i_rdev),
212 			  minor(inode->u.i_rdev));
213 		inode->u.i_rdev = erofs_new_encode_dev(inode->u.i_rdev);
214 		return 0;
215 	case S_IFDIR:
216 		return erofs_init_empty_dir(inode);
217 	case S_IFLNK: {
218 		int ret;
219 
220 		inode->i_link = malloc(inode->i_size + 1);
221 		if (!inode->i_link)
222 			return -ENOMEM;
223 		ret = erofs_pread(inode, inode->i_link, inode->i_size, 0);
224 		erofs_dbg("\tsymlink: %s -> %s", inode->i_srcpath, inode->i_link);
225 		return ret;
226 	}
227 	case S_IFREG:
228 		if (inode->i_size)
229 			return erofs_rebuild_fixup_inode_index(inode);
230 		return 0;
231 	default:
232 		break;
233 	}
234 	return -EINVAL;
235 }
236 
237 /*
238  * @parent:  parent directory in inode tree
239  * @ctx.dir: parent directory when itering erofs_iterate_dir()
240  */
241 struct erofs_rebuild_dir_context {
242 	struct erofs_dir_context ctx;
243 	struct erofs_inode *parent;
244 };
245 
erofs_rebuild_dirent_iter(struct erofs_dir_context * ctx)246 static int erofs_rebuild_dirent_iter(struct erofs_dir_context *ctx)
247 {
248 	struct erofs_rebuild_dir_context *rctx = (void *)ctx;
249 	struct erofs_inode *parent = rctx->parent;
250 	struct erofs_inode *dir = ctx->dir;
251 	struct erofs_inode *inode, *candidate;
252 	struct erofs_inode src;
253 	struct erofs_dentry *d;
254 	char *path, *dname;
255 	bool dumb;
256 	int ret;
257 
258 	if (ctx->dot_dotdot)
259 		return 0;
260 
261 	ret = asprintf(&path, "%s/%.*s", rctx->parent->i_srcpath,
262 		       ctx->de_namelen, ctx->dname);
263 	if (ret < 0)
264 		return ret;
265 
266 	erofs_dbg("parsing %s", path);
267 	dname = path + strlen(parent->i_srcpath) + 1;
268 
269 	d = erofs_rebuild_get_dentry(parent, dname, false,
270 				     &dumb, &dumb, false);
271 	if (IS_ERR(d)) {
272 		ret = PTR_ERR(d);
273 		goto out;
274 	}
275 
276 	ret = 0;
277 	if (d->type != EROFS_FT_UNKNOWN) {
278 		/*
279 		 * bail out if the file exists in the upper layers.  (Note that
280 		 * extended attributes won't be merged too even for dirs.)
281 		 */
282 		if (!S_ISDIR(d->inode->i_mode) || d->inode->opaque)
283 			goto out;
284 
285 		/* merge directory entries */
286 		src = (struct erofs_inode) {
287 			.sbi = dir->sbi,
288 			.nid = ctx->de_nid
289 		};
290 		ret = erofs_read_inode_from_disk(&src);
291 		if (ret || !S_ISDIR(src.i_mode))
292 			goto out;
293 		parent = d->inode;
294 		inode = dir = &src;
295 	} else {
296 		u64 nid;
297 
298 		DBG_BUGON(parent != d->inode);
299 		inode = erofs_new_inode();
300 		if (IS_ERR(inode)) {
301 			ret = PTR_ERR(inode);
302 			goto out;
303 		}
304 
305 		/* reuse i_ino[0] to read nid in source fs */
306 		nid = inode->i_ino[0];
307 		inode->sbi = dir->sbi;
308 		inode->nid = ctx->de_nid;
309 		ret = erofs_read_inode_from_disk(inode);
310 		if (ret)
311 			goto out;
312 
313 		/* restore nid in new generated fs */
314 		inode->i_ino[1] = inode->i_ino[0];
315 		inode->i_ino[0] = nid;
316 		inode->dev = inode->sbi->dev;
317 
318 		if (S_ISREG(inode->i_mode) && inode->i_nlink > 1 &&
319 		    (candidate = erofs_iget(inode->dev, ctx->de_nid))) {
320 			/* hardlink file */
321 			erofs_iput(inode);
322 			inode = candidate;
323 			if (S_ISDIR(inode->i_mode)) {
324 				erofs_err("hardlink directory not supported");
325 				ret = -EISDIR;
326 				goto out;
327 			}
328 			inode->i_nlink++;
329 			erofs_dbg("\thardlink: %s -> %s", path, inode->i_srcpath);
330 		} else {
331 			ret = erofs_read_xattrs_from_disk(inode);
332 			if (ret) {
333 				erofs_iput(inode);
334 				goto out;
335 			}
336 
337 			inode->i_parent = d->inode;
338 			inode->i_srcpath = path;
339 			path = NULL;
340 			inode->i_ino[1] = inode->nid;
341 			inode->i_nlink = 1;
342 
343 			ret = erofs_rebuild_fill_inode(inode);
344 			if (ret) {
345 				erofs_iput(inode);
346 				goto out;
347 			}
348 
349 			erofs_insert_ihash(inode, inode->dev, inode->i_ino[1]);
350 			parent = dir = inode;
351 		}
352 
353 		d->inode = inode;
354 		d->type = erofs_mode_to_ftype(inode->i_mode);
355 	}
356 
357 	if (S_ISDIR(inode->i_mode)) {
358 		struct erofs_rebuild_dir_context nctx = *rctx;
359 
360 		nctx.parent = parent;
361 		nctx.ctx.dir = dir;
362 		ret = erofs_iterate_dir(&nctx.ctx, false);
363 		if (ret)
364 			goto out;
365 	}
366 
367 	/* reset sbi, nid after subdirs are all loaded for the final dump */
368 	inode->sbi = &sbi;
369 	inode->nid = 0;
370 out:
371 	free(path);
372 	return ret;
373 }
374 
erofs_rebuild_load_tree(struct erofs_inode * root,struct erofs_sb_info * sbi)375 int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi)
376 {
377 	struct erofs_inode inode = {};
378 	struct erofs_rebuild_dir_context ctx;
379 	int ret;
380 
381 	if (!sbi->devname) {
382 		erofs_err("failed to find a device for rebuilding");
383 		return -EINVAL;
384 	}
385 
386 	ret = erofs_read_superblock(sbi);
387 	if (ret) {
388 		erofs_err("failed to read superblock of %s", sbi->devname);
389 		return ret;
390 	}
391 
392 	inode.nid = sbi->root_nid;
393 	inode.sbi = sbi;
394 	ret = erofs_read_inode_from_disk(&inode);
395 	if (ret) {
396 		erofs_err("failed to read root inode of %s", sbi->devname);
397 		return ret;
398 	}
399 	inode.i_srcpath = strdup("/");
400 
401 	ctx = (struct erofs_rebuild_dir_context) {
402 		.ctx.dir = &inode,
403 		.ctx.cb = erofs_rebuild_dirent_iter,
404 		.parent = root,
405 	};
406 	ret = erofs_iterate_dir(&ctx.ctx, false);
407 	free(inode.i_srcpath);
408 	return ret;
409 }
410