• 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/dir.h"
15 #include "erofs/xattr.h"
16 #include "erofs/blobchunk.h"
17 #include "erofs/internal.h"
18 #include "liberofs_uuid.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(dir->sbi);
35 	if (IS_ERR(inode))
36 		return ERR_CAST(inode);
37 
38 	if (asprintf(&inode->i_srcpath, "%s/%s",
39 		     dir->i_srcpath ? : "", s) < 0) {
40 		erofs_iput(inode);
41 		return ERR_PTR(-ENOMEM);
42 	}
43 	inode->i_mode = S_IFDIR | 0755;
44 	inode->i_parent = dir;
45 	inode->i_uid = getuid();
46 	inode->i_gid = getgid();
47 	inode->i_mtime = inode->sbi->build_time;
48 	inode->i_mtime_nsec = inode->sbi->build_time_nsec;
49 	inode->dev = dir->dev;
50 	erofs_init_empty_dir(inode);
51 
52 	d = erofs_d_alloc(dir, s);
53 	if (IS_ERR(d)) {
54 		erofs_iput(inode);
55 	} else {
56 		d->type = EROFS_FT_DIR;
57 		d->inode = inode;
58 	}
59 	return d;
60 }
61 
erofs_rebuild_get_dentry(struct erofs_inode * pwd,char * path,bool aufs,bool * whout,bool * opq,bool to_head)62 struct erofs_dentry *erofs_rebuild_get_dentry(struct erofs_inode *pwd,
63 		char *path, bool aufs, bool *whout, bool *opq, bool to_head)
64 {
65 	struct erofs_dentry *d = NULL;
66 	unsigned int len = strlen(path);
67 	char *s = path;
68 
69 	*whout = false;
70 	*opq = false;
71 
72 	while (s < path + len) {
73 		char *slash = memchr(s, '/', path + len - s);
74 
75 		if (slash) {
76 			if (s == slash) {
77 				while (*++s == '/');	/* skip '//...' */
78 				continue;
79 			}
80 			*slash = '\0';
81 		}
82 
83 		if (!memcmp(s, ".", 2)) {
84 			/* null */
85 		} else if (!memcmp(s, "..", 3)) {
86 			pwd = pwd->i_parent;
87 		} else {
88 			struct erofs_inode *inode = NULL;
89 
90 			if (aufs && !slash) {
91 				if (!memcmp(s, AUFS_WH_DIROPQ, sizeof(AUFS_WH_DIROPQ))) {
92 					*opq = true;
93 					break;
94 				}
95 				if (!memcmp(s, AUFS_WH_PFX, sizeof(AUFS_WH_PFX) - 1)) {
96 					s += sizeof(AUFS_WH_PFX) - 1;
97 					*whout = true;
98 				}
99 			}
100 
101 			list_for_each_entry(d, &pwd->i_subdirs, d_child) {
102 				if (!strcmp(d->name, s)) {
103 					if (d->type != EROFS_FT_DIR && slash)
104 						return ERR_PTR(-EIO);
105 					inode = d->inode;
106 					break;
107 				}
108 			}
109 
110 			if (inode) {
111 				if (to_head) {
112 					list_del(&d->d_child);
113 					list_add(&d->d_child, &pwd->i_subdirs);
114 				}
115 				pwd = inode;
116 			} else if (!slash) {
117 				d = erofs_d_alloc(pwd, s);
118 				if (IS_ERR(d))
119 					return d;
120 				d->type = EROFS_FT_UNKNOWN;
121 				d->inode = pwd;
122 			} else {
123 				d = erofs_rebuild_mkdir(pwd, s);
124 				if (IS_ERR(d))
125 					return d;
126 				pwd = d->inode;
127 			}
128 		}
129 		if (slash) {
130 			*slash = '/';
131 			s = slash + 1;
132 		} else {
133 			break;
134 		}
135 	}
136 	return d;
137 }
138 
erofs_rebuild_write_blob_index(struct erofs_sb_info * dst_sb,struct erofs_inode * inode)139 static int erofs_rebuild_write_blob_index(struct erofs_sb_info *dst_sb,
140 					  struct erofs_inode *inode)
141 {
142 	int ret;
143 	unsigned int count, unit, chunkbits, i;
144 	struct erofs_inode_chunk_index *idx;
145 	erofs_off_t chunksize;
146 	erofs_blk_t blkaddr;
147 
148 	/* TODO: fill data map in other layouts */
149 	if (inode->datalayout == EROFS_INODE_CHUNK_BASED) {
150 		chunkbits = inode->u.chunkbits;
151 		if (chunkbits < dst_sb->blkszbits) {
152 			erofs_err("%s: chunk size %u is smaller than the target block size %u",
153 				  inode->i_srcpath, 1U << chunkbits,
154 				  1U << dst_sb->blkszbits);
155 			return -EINVAL;
156 		}
157 	} else if (inode->datalayout == EROFS_INODE_FLAT_PLAIN) {
158 		chunkbits = ilog2(inode->i_size - 1) + 1;
159 		if (chunkbits < dst_sb->blkszbits)
160 			chunkbits = dst_sb->blkszbits;
161 		if (chunkbits - dst_sb->blkszbits > EROFS_CHUNK_FORMAT_BLKBITS_MASK)
162 			chunkbits = EROFS_CHUNK_FORMAT_BLKBITS_MASK + dst_sb->blkszbits;
163 	} else {
164 		erofs_err("%s: unsupported datalayout %d ", inode->i_srcpath,
165 			  inode->datalayout);
166 		return -EOPNOTSUPP;
167 	}
168 
169 	chunksize = 1ULL << chunkbits;
170 	count = DIV_ROUND_UP(inode->i_size, chunksize);
171 
172 	unit = sizeof(struct erofs_inode_chunk_index);
173 	inode->extent_isize = count * unit;
174 	idx = malloc(max(sizeof(*idx), sizeof(void *)));
175 	if (!idx)
176 		return -ENOMEM;
177 	inode->chunkindexes = idx;
178 
179 	for (i = 0; i < count; i++) {
180 		struct erofs_blobchunk *chunk;
181 		struct erofs_map_blocks map = {
182 			.index = UINT_MAX,
183 		};
184 
185 		map.m_la = i << chunkbits;
186 		ret = erofs_map_blocks(inode, &map, 0);
187 		if (ret)
188 			goto err;
189 
190 		blkaddr = erofs_blknr(dst_sb, map.m_pa);
191 		chunk = erofs_get_unhashed_chunk(inode->dev, blkaddr, 0);
192 		if (IS_ERR(chunk)) {
193 			ret = PTR_ERR(chunk);
194 			goto err;
195 		}
196 		*(void **)idx++ = chunk;
197 
198 	}
199 	inode->datalayout = EROFS_INODE_CHUNK_BASED;
200 	inode->u.chunkformat = EROFS_CHUNK_FORMAT_INDEXES;
201 	inode->u.chunkformat |= chunkbits - dst_sb->blkszbits;
202 	return 0;
203 err:
204 	free(inode->chunkindexes);
205 	inode->chunkindexes = NULL;
206 	return ret;
207 }
208 
erofs_rebuild_update_inode(struct erofs_sb_info * dst_sb,struct erofs_inode * inode,enum erofs_rebuild_datamode datamode)209 static int erofs_rebuild_update_inode(struct erofs_sb_info *dst_sb,
210 				      struct erofs_inode *inode,
211 				      enum erofs_rebuild_datamode datamode)
212 {
213 	int err = 0;
214 
215 	switch (inode->i_mode & S_IFMT) {
216 	case S_IFCHR:
217 		if (erofs_inode_is_whiteout(inode))
218 			inode->i_parent->whiteouts = true;
219 		/* fallthrough */
220 	case S_IFBLK:
221 	case S_IFIFO:
222 	case S_IFSOCK:
223 		inode->i_size = 0;
224 		erofs_dbg("\tdev: %d %d", major(inode->u.i_rdev),
225 			  minor(inode->u.i_rdev));
226 		inode->u.i_rdev = erofs_new_encode_dev(inode->u.i_rdev);
227 		break;
228 	case S_IFDIR:
229 		err = erofs_init_empty_dir(inode);
230 		break;
231 	case S_IFLNK:
232 		inode->i_link = malloc(inode->i_size + 1);
233 		if (!inode->i_link)
234 			return -ENOMEM;
235 		err = erofs_pread(inode, inode->i_link, inode->i_size, 0);
236 		erofs_dbg("\tsymlink: %s -> %s", inode->i_srcpath, inode->i_link);
237 		break;
238 	case S_IFREG:
239 		if (!inode->i_size) {
240 			inode->u.i_blkaddr = NULL_ADDR;
241 			break;
242 		}
243 		if (datamode == EROFS_REBUILD_DATA_BLOB_INDEX)
244 			err = erofs_rebuild_write_blob_index(dst_sb, inode);
245 		else if (datamode == EROFS_REBUILD_DATA_RESVSP)
246 			inode->datasource = EROFS_INODE_DATA_SOURCE_RESVSP;
247 		else
248 			err = -EOPNOTSUPP;
249 		break;
250 	default:
251 		return -EINVAL;
252 	}
253 	return err;
254 }
255 
256 /*
257  * @mergedir: parent directory in the merged tree
258  * @ctx.dir:  parent directory when itering erofs_iterate_dir()
259  * @datamode: indicate how to import inode data
260  */
261 struct erofs_rebuild_dir_context {
262 	struct erofs_dir_context ctx;
263 	struct erofs_inode *mergedir;
264 	enum erofs_rebuild_datamode datamode;
265 };
266 
erofs_rebuild_dirent_iter(struct erofs_dir_context * ctx)267 static int erofs_rebuild_dirent_iter(struct erofs_dir_context *ctx)
268 {
269 	struct erofs_rebuild_dir_context *rctx = (void *)ctx;
270 	struct erofs_inode *mergedir = rctx->mergedir;
271 	struct erofs_inode *dir = ctx->dir;
272 	struct erofs_inode *inode, *candidate;
273 	struct erofs_inode src;
274 	struct erofs_dentry *d;
275 	char *path, *dname;
276 	bool dumb;
277 	int ret;
278 
279 	if (ctx->dot_dotdot)
280 		return 0;
281 
282 	ret = asprintf(&path, "%s/%.*s", rctx->mergedir->i_srcpath,
283 		       ctx->de_namelen, ctx->dname);
284 	if (ret < 0)
285 		return ret;
286 
287 	erofs_dbg("parsing %s", path);
288 	dname = path + strlen(mergedir->i_srcpath) + 1;
289 
290 	d = erofs_rebuild_get_dentry(mergedir, dname, false,
291 				     &dumb, &dumb, false);
292 	if (IS_ERR(d)) {
293 		ret = PTR_ERR(d);
294 		goto out;
295 	}
296 
297 	ret = 0;
298 	if (d->type != EROFS_FT_UNKNOWN) {
299 		/*
300 		 * bail out if the file exists in the upper layers.  (Note that
301 		 * extended attributes won't be merged too even for dirs.)
302 		 */
303 		if (!S_ISDIR(d->inode->i_mode) || d->inode->opaque)
304 			goto out;
305 
306 		/* merge directory entries */
307 		src = (struct erofs_inode) {
308 			.sbi = dir->sbi,
309 			.nid = ctx->de_nid
310 		};
311 		ret = erofs_read_inode_from_disk(&src);
312 		if (ret || !S_ISDIR(src.i_mode))
313 			goto out;
314 		mergedir = d->inode;
315 		inode = dir = &src;
316 	} else {
317 		u64 nid;
318 
319 		DBG_BUGON(mergedir != d->inode);
320 		inode = erofs_new_inode(dir->sbi);
321 		if (IS_ERR(inode)) {
322 			ret = PTR_ERR(inode);
323 			goto out;
324 		}
325 
326 		/* reuse i_ino[0] to read nid in source fs */
327 		nid = inode->i_ino[0];
328 		inode->sbi = dir->sbi;
329 		inode->nid = ctx->de_nid;
330 		ret = erofs_read_inode_from_disk(inode);
331 		if (ret)
332 			goto out;
333 
334 		/* restore nid in new generated fs */
335 		inode->i_ino[1] = inode->i_ino[0];
336 		inode->i_ino[0] = nid;
337 		inode->dev = inode->sbi->dev;
338 
339 		if (S_ISREG(inode->i_mode) && inode->i_nlink > 1 &&
340 		    (candidate = erofs_iget(inode->dev, ctx->de_nid))) {
341 			/* hardlink file */
342 			erofs_iput(inode);
343 			inode = candidate;
344 			if (S_ISDIR(inode->i_mode)) {
345 				erofs_err("hardlink directory not supported");
346 				ret = -EISDIR;
347 				goto out;
348 			}
349 			inode->i_nlink++;
350 			erofs_dbg("\thardlink: %s -> %s", path, inode->i_srcpath);
351 		} else {
352 			ret = erofs_read_xattrs_from_disk(inode);
353 			if (ret) {
354 				erofs_iput(inode);
355 				goto out;
356 			}
357 
358 			inode->i_parent = d->inode;
359 			inode->i_srcpath = path;
360 			path = NULL;
361 			inode->i_ino[1] = inode->nid;
362 			inode->i_nlink = 1;
363 
364 			ret = erofs_rebuild_update_inode(&g_sbi, inode,
365 							 rctx->datamode);
366 			if (ret) {
367 				erofs_iput(inode);
368 				goto out;
369 			}
370 
371 			erofs_insert_ihash(inode);
372 			mergedir = dir = inode;
373 		}
374 
375 		d->inode = inode;
376 		d->type = erofs_mode_to_ftype(inode->i_mode);
377 	}
378 
379 	if (S_ISDIR(inode->i_mode)) {
380 		struct erofs_rebuild_dir_context nctx = *rctx;
381 
382 		nctx.mergedir = mergedir;
383 		nctx.ctx.dir = dir;
384 		ret = erofs_iterate_dir(&nctx.ctx, false);
385 		if (ret)
386 			goto out;
387 	}
388 
389 	/* reset sbi, nid after subdirs are all loaded for the final dump */
390 	inode->sbi = &g_sbi;
391 	inode->nid = 0;
392 out:
393 	free(path);
394 	return ret;
395 }
396 
erofs_rebuild_load_tree(struct erofs_inode * root,struct erofs_sb_info * sbi,enum erofs_rebuild_datamode mode)397 int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi,
398 			    enum erofs_rebuild_datamode mode)
399 {
400 	struct erofs_inode inode = {};
401 	struct erofs_rebuild_dir_context ctx;
402 	char uuid_str[37];
403 	char *fsid = sbi->devname;
404 	int ret;
405 
406 	if (!fsid) {
407 		erofs_uuid_unparse_lower(sbi->uuid, uuid_str);
408 		fsid = uuid_str;
409 	}
410 	ret = erofs_read_superblock(sbi);
411 	if (ret) {
412 		erofs_err("failed to read superblock of %s", fsid);
413 		return ret;
414 	}
415 
416 	inode.nid = sbi->root_nid;
417 	inode.sbi = sbi;
418 	ret = erofs_read_inode_from_disk(&inode);
419 	if (ret) {
420 		erofs_err("failed to read root inode of %s", fsid);
421 		return ret;
422 	}
423 	inode.i_srcpath = strdup("/");
424 
425 	ctx = (struct erofs_rebuild_dir_context) {
426 		.ctx.dir = &inode,
427 		.ctx.cb = erofs_rebuild_dirent_iter,
428 		.mergedir = root,
429 		.datamode = mode,
430 	};
431 	ret = erofs_iterate_dir(&ctx.ctx, false);
432 	free(inode.i_srcpath);
433 	return ret;
434 }
435 
erofs_rebuild_basedir_dirent_iter(struct erofs_dir_context * ctx)436 static int erofs_rebuild_basedir_dirent_iter(struct erofs_dir_context *ctx)
437 {
438 	struct erofs_rebuild_dir_context *rctx = (void *)ctx;
439 	struct erofs_inode *dir = ctx->dir;
440 	struct erofs_inode *mergedir = rctx->mergedir;
441 	struct erofs_dentry *d;
442 	char *dname;
443 	bool dumb;
444 	int ret;
445 
446 	if (ctx->dot_dotdot)
447 		return 0;
448 
449 	dname = strndup(ctx->dname, ctx->de_namelen);
450 	if (!dname)
451 		return -ENOMEM;
452 	d = erofs_rebuild_get_dentry(mergedir, dname, false,
453 				     &dumb, &dumb, false);
454 	if (IS_ERR(d)) {
455 		ret = PTR_ERR(d);
456 		goto out;
457 	}
458 
459 	if (d->type == EROFS_FT_UNKNOWN) {
460 		d->nid = ctx->de_nid;
461 		d->type = ctx->de_ftype;
462 		d->validnid = true;
463 		if (!mergedir->whiteouts && erofs_dentry_is_wht(dir->sbi, d))
464 			mergedir->whiteouts = true;
465 	} else {
466 		struct erofs_inode *inode = d->inode;
467 
468 		/* update sub-directories only for recursively loading */
469 		if (S_ISDIR(inode->i_mode) &&
470 		    (ctx->de_ftype == EROFS_FT_DIR ||
471 		     ctx->de_ftype == EROFS_FT_UNKNOWN)) {
472 			list_del(&inode->i_hash);
473 			inode->dev = dir->sbi->dev;
474 			inode->i_ino[1] = ctx->de_nid;
475 			erofs_insert_ihash(inode);
476 		}
477 	}
478 	ret = 0;
479 out:
480 	free(dname);
481 	return ret;
482 }
483 
erofs_rebuild_load_basedir(struct erofs_inode * dir)484 int erofs_rebuild_load_basedir(struct erofs_inode *dir)
485 {
486 	struct erofs_inode fakeinode = {
487 		.sbi = dir->sbi,
488 		.nid = dir->i_ino[1],
489 	};
490 	struct erofs_rebuild_dir_context ctx;
491 	int ret;
492 
493 	ret = erofs_read_inode_from_disk(&fakeinode);
494 	if (ret) {
495 		erofs_err("failed to read inode @ %llu", fakeinode.nid);
496 		return ret;
497 	}
498 
499 	/* Inherit the maximum xattr size for the root directory */
500 	if (__erofs_unlikely(IS_ROOT(dir)))
501 		dir->xattr_isize = fakeinode.xattr_isize;
502 
503 	/*
504 	 * May be triggered if ftype == EROFS_FT_UNKNOWN, which is impossible
505 	 * with the current mkfs.
506 	 */
507 	if (__erofs_unlikely(!S_ISDIR(fakeinode.i_mode))) {
508 		DBG_BUGON(1);
509 		return 0;
510 	}
511 
512 	ctx = (struct erofs_rebuild_dir_context) {
513 		.ctx.dir = &fakeinode,
514 		.ctx.cb = erofs_rebuild_basedir_dirent_iter,
515 		.mergedir = dir,
516 	};
517 	return erofs_iterate_dir(&ctx.ctx, false);
518 }
519