• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 #include "erofs/print.h"
3 #include "erofs/dir.h"
4 #include <stdlib.h>
5 #include <sys/stat.h>
6 
traverse_dirents(struct erofs_dir_context * ctx,void * dentry_blk,unsigned int lblk,unsigned int next_nameoff,unsigned int maxsize,bool fsck)7 static int traverse_dirents(struct erofs_dir_context *ctx,
8 			    void *dentry_blk, unsigned int lblk,
9 			    unsigned int next_nameoff, unsigned int maxsize,
10 			    bool fsck)
11 {
12 	struct erofs_dirent *de = dentry_blk;
13 	const struct erofs_dirent *end = dentry_blk + next_nameoff;
14 	const char *prev_name = NULL;
15 	const char *errmsg;
16 	unsigned int prev_namelen = 0;
17 	int ret = 0;
18 	bool silent = false;
19 
20 	while (de < end) {
21 		const char *de_name;
22 		unsigned int de_namelen;
23 		unsigned int nameoff;
24 
25 		nameoff = le16_to_cpu(de->nameoff);
26 		de_name = (char *)dentry_blk + nameoff;
27 
28 		/* the last dirent check */
29 		if (de + 1 >= end)
30 			de_namelen = strnlen(de_name, maxsize - nameoff);
31 		else
32 			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
33 
34 		ctx->de_nid = le64_to_cpu(de->nid);
35 		erofs_dbg("traversed nid (%llu)", ctx->de_nid | 0ULL);
36 
37 		ret = -EFSCORRUPTED;
38 		/* corrupted entry check */
39 		if (nameoff != next_nameoff) {
40 			errmsg = "bogus dirent nameoff";
41 			break;
42 		}
43 
44 		if (nameoff + de_namelen > maxsize ||
45 				de_namelen > EROFS_NAME_LEN) {
46 			errmsg = "bogus dirent namelen";
47 			break;
48 		}
49 
50 		if (fsck && prev_name) {
51 			int cmp = strncmp(prev_name, de_name,
52 					  min(prev_namelen, de_namelen));
53 
54 			if (cmp > 0 || (cmp == 0 &&
55 					prev_namelen >= de_namelen)) {
56 				errmsg = "wrong dirent name order";
57 				break;
58 			}
59 		}
60 
61 		if (fsck && de->file_type >= EROFS_FT_MAX) {
62 			errmsg = "invalid file type %u";
63 			break;
64 		}
65 
66 		ctx->dname = de_name;
67 		ctx->de_namelen = de_namelen;
68 		ctx->de_ftype = de->file_type;
69 		ctx->dot_dotdot = is_dot_dotdot_len(de_name, de_namelen);
70 		if (ctx->dot_dotdot) {
71 			switch (de_namelen) {
72 			case 2:
73 				if (fsck &&
74 				    (ctx->flags & EROFS_READDIR_DOTDOT_FOUND)) {
75 					errmsg = "duplicated `..' dirent";
76 					goto out;
77 				}
78 				ctx->flags |= EROFS_READDIR_DOTDOT_FOUND;
79 				if (sbi.root_nid == ctx->dir->nid) {
80 					ctx->pnid = sbi.root_nid;
81 					ctx->flags |= EROFS_READDIR_VALID_PNID;
82 				}
83 				if (fsck &&
84 				    (ctx->flags & EROFS_READDIR_VALID_PNID) &&
85 				    ctx->de_nid != ctx->pnid) {
86 					errmsg = "corrupted `..' dirent";
87 					goto out;
88 				}
89 				break;
90 			case 1:
91 				if (fsck &&
92 				    (ctx->flags & EROFS_READDIR_DOT_FOUND)) {
93 					errmsg = "duplicated `.' dirent";
94 					goto out;
95 				}
96 
97 				ctx->flags |= EROFS_READDIR_DOT_FOUND;
98 				if (fsck && ctx->de_nid != ctx->dir->nid) {
99 					errmsg = "corrupted `.' dirent";
100 					goto out;
101 				}
102 				break;
103 			}
104 		}
105 		ret = ctx->cb(ctx);
106 		if (ret) {
107 			silent = true;
108 			break;
109 		}
110 		prev_name = de_name;
111 		prev_namelen = de_namelen;
112 		next_nameoff += de_namelen;
113 		++de;
114 	}
115 out:
116 	if (ret && !silent)
117 		erofs_err("%s @ nid %llu, lblk %u, index %lu",
118 			  errmsg, ctx->dir->nid | 0ULL, lblk,
119 			  (de - (struct erofs_dirent *)dentry_blk) | 0UL);
120 	return ret;
121 }
122 
erofs_iterate_dir(struct erofs_dir_context * ctx,bool fsck)123 int erofs_iterate_dir(struct erofs_dir_context *ctx, bool fsck)
124 {
125 	struct erofs_inode *dir = ctx->dir;
126 	int err = 0;
127 	erofs_off_t pos;
128 	char buf[EROFS_BLKSIZ];
129 
130 	if ((dir->i_mode & S_IFMT) != S_IFDIR)
131 		return -ENOTDIR;
132 
133 	ctx->flags &= ~EROFS_READDIR_ALL_SPECIAL_FOUND;
134 	pos = 0;
135 	while (pos < dir->i_size) {
136 		erofs_blk_t lblk = erofs_blknr(pos);
137 		erofs_off_t maxsize = min_t(erofs_off_t,
138 					dir->i_size - pos, EROFS_BLKSIZ);
139 		const struct erofs_dirent *de = (const void *)buf;
140 		unsigned int nameoff;
141 
142 		err = erofs_pread(dir, buf, maxsize, pos);
143 		if (err) {
144 			erofs_err("I/O error occurred when reading dirents @ nid %llu, lblk %u: %d",
145 				  dir->nid | 0ULL, lblk, err);
146 			return err;
147 		}
148 
149 		nameoff = le16_to_cpu(de->nameoff);
150 		if (nameoff < sizeof(struct erofs_dirent) ||
151 		    nameoff >= PAGE_SIZE) {
152 			erofs_err("invalid de[0].nameoff %u @ nid %llu, lblk %u",
153 				  nameoff, dir->nid | 0ULL, lblk);
154 			return -EFSCORRUPTED;
155 		}
156 		err = traverse_dirents(ctx, buf, lblk, nameoff, maxsize, fsck);
157 		if (err)
158 			break;
159 		pos += maxsize;
160 	}
161 
162 	if (fsck && (ctx->flags & EROFS_READDIR_ALL_SPECIAL_FOUND) !=
163 			EROFS_READDIR_ALL_SPECIAL_FOUND) {
164 		erofs_err("`.' or `..' dirent is missing @ nid %llu",
165 			  dir->nid | 0ULL);
166 		return -EFSCORRUPTED;
167 	}
168 	return err;
169 }
170