• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Created by Li Guifu <blucerlee@gmail.com>
4  */
5 #include <stdlib.h>
6 #include <string.h>
7 #include <signal.h>
8 #include <libgen.h>
9 #include <fuse.h>
10 #include <fuse_opt.h>
11 #include "macosx.h"
12 #include "erofs/config.h"
13 #include "erofs/print.h"
14 #include "erofs/io.h"
15 #include "erofs/dir.h"
16 #include "erofs/inode.h"
17 
18 struct erofsfuse_dir_context {
19 	struct erofs_dir_context ctx;
20 	fuse_fill_dir_t filler;
21 	struct fuse_file_info *fi;
22 	void *buf;
23 };
24 
erofsfuse_fill_dentries(struct erofs_dir_context * ctx)25 static int erofsfuse_fill_dentries(struct erofs_dir_context *ctx)
26 {
27 	struct erofsfuse_dir_context *fusectx = (void *)ctx;
28 	struct stat st = {0};
29 	char dname[EROFS_NAME_LEN + 1];
30 
31 	strncpy(dname, ctx->dname, ctx->de_namelen);
32 	dname[ctx->de_namelen] = '\0';
33 	st.st_mode = erofs_ftype_to_dtype(ctx->de_ftype) << 12;
34 	fusectx->filler(fusectx->buf, dname, &st, 0);
35 	return 0;
36 }
37 
erofsfuse_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)38 int erofsfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
39 		      off_t offset, struct fuse_file_info *fi)
40 {
41 	int ret;
42 	struct erofs_inode dir;
43 	struct erofsfuse_dir_context ctx = {
44 		.ctx.dir = &dir,
45 		.ctx.cb = erofsfuse_fill_dentries,
46 		.filler = filler,
47 		.fi = fi,
48 		.buf = buf,
49 	};
50 	erofs_dbg("readdir:%s offset=%llu", path, (long long)offset);
51 
52 	dir.sbi = &sbi;
53 	ret = erofs_ilookup(path, &dir);
54 	if (ret)
55 		return ret;
56 
57 	erofs_dbg("path=%s nid = %llu", path, dir.nid | 0ULL);
58 	if (!S_ISDIR(dir.i_mode))
59 		return -ENOTDIR;
60 
61 	if (!dir.i_size)
62 		return 0;
63 #ifdef NDEBUG
64 	return erofs_iterate_dir(&ctx.ctx, false);
65 #else
66 	return erofs_iterate_dir(&ctx.ctx, true);
67 #endif
68 }
69 
erofsfuse_init(struct fuse_conn_info * info)70 static void *erofsfuse_init(struct fuse_conn_info *info)
71 {
72 	erofs_info("Using FUSE protocol %d.%d", info->proto_major, info->proto_minor);
73 	return NULL;
74 }
75 
erofsfuse_open(const char * path,struct fuse_file_info * fi)76 static int erofsfuse_open(const char *path, struct fuse_file_info *fi)
77 {
78 	erofs_dbg("open path=%s", path);
79 
80 	if ((fi->flags & O_ACCMODE) != O_RDONLY)
81 		return -EACCES;
82 
83 	return 0;
84 }
85 
erofsfuse_getattr(const char * path,struct stat * stbuf)86 static int erofsfuse_getattr(const char *path, struct stat *stbuf)
87 {
88 	struct erofs_inode vi = { .sbi = &sbi };
89 	int ret;
90 
91 	erofs_dbg("getattr(%s)", path);
92 	ret = erofs_ilookup(path, &vi);
93 	if (ret)
94 		return -ENOENT;
95 
96 	stbuf->st_mode  = vi.i_mode;
97 	stbuf->st_nlink = vi.i_nlink;
98 	stbuf->st_size  = vi.i_size;
99 	stbuf->st_blocks = roundup(vi.i_size, erofs_blksiz(vi.sbi)) >> 9;
100 	stbuf->st_uid = vi.i_uid;
101 	stbuf->st_gid = vi.i_gid;
102 	if (S_ISBLK(vi.i_mode) || S_ISCHR(vi.i_mode))
103 		stbuf->st_rdev = vi.u.i_rdev;
104 	stbuf->st_ctime = vi.i_mtime;
105 	stbuf->st_mtime = stbuf->st_ctime;
106 	stbuf->st_atime = stbuf->st_ctime;
107 	return 0;
108 }
109 
erofsfuse_read(const char * path,char * buffer,size_t size,off_t offset,struct fuse_file_info * fi)110 static int erofsfuse_read(const char *path, char *buffer,
111 			  size_t size, off_t offset,
112 			  struct fuse_file_info *fi)
113 {
114 	int ret;
115 	struct erofs_inode vi;
116 
117 	erofs_dbg("path:%s size=%zd offset=%llu", path, size, (long long)offset);
118 
119 	vi.sbi = &sbi;
120 	ret = erofs_ilookup(path, &vi);
121 	if (ret)
122 		return ret;
123 
124 	ret = erofs_pread(&vi, buffer, size, offset);
125 	if (ret)
126 		return ret;
127 	if (offset >= vi.i_size)
128 		return 0;
129 	if (offset + size > vi.i_size)
130 		return vi.i_size - offset;
131 	return size;
132 }
133 
erofsfuse_readlink(const char * path,char * buffer,size_t size)134 static int erofsfuse_readlink(const char *path, char *buffer, size_t size)
135 {
136 	int ret = erofsfuse_read(path, buffer, size, 0, NULL);
137 
138 	if (ret < 0)
139 		return ret;
140 	DBG_BUGON(ret > size);
141 	if (ret == size)
142 		buffer[size - 1] = '\0';
143 	erofs_dbg("readlink(%s): %s", path, buffer);
144 	return 0;
145 }
146 
erofsfuse_getxattr(const char * path,const char * name,char * value,size_t size,uint32_t position)147 static int erofsfuse_getxattr(const char *path, const char *name, char *value,
148 			size_t size
149 #ifdef __APPLE__
150 			, uint32_t position)
151 #else
152 			)
153 #endif
154 {
155 	int ret;
156 	struct erofs_inode vi;
157 
158 	erofs_dbg("getxattr(%s): name=%s size=%llu", path, name, size);
159 
160 	vi.sbi = &sbi;
161 	ret = erofs_ilookup(path, &vi);
162 	if (ret)
163 		return ret;
164 
165 	return erofs_getxattr(&vi, name, value, size);
166 }
167 
erofsfuse_listxattr(const char * path,char * list,size_t size)168 static int erofsfuse_listxattr(const char *path, char *list, size_t size)
169 {
170 	int ret;
171 	struct erofs_inode vi;
172 
173 	erofs_dbg("listxattr(%s): size=%llu", path, size);
174 
175 	vi.sbi = &sbi;
176 	ret = erofs_ilookup(path, &vi);
177 	if (ret)
178 		return ret;
179 
180 	return erofs_listxattr(&vi, list, size);
181 }
182 
183 static struct fuse_operations erofs_ops = {
184 	.getxattr = erofsfuse_getxattr,
185 	.listxattr = erofsfuse_listxattr,
186 	.readlink = erofsfuse_readlink,
187 	.getattr = erofsfuse_getattr,
188 	.readdir = erofsfuse_readdir,
189 	.open = erofsfuse_open,
190 	.read = erofsfuse_read,
191 	.init = erofsfuse_init,
192 };
193 
194 static struct options {
195 	const char *disk;
196 	const char *mountpoint;
197 	u64 offset;
198 	unsigned int debug_lvl;
199 	bool show_help;
200 	bool odebug;
201 } fusecfg;
202 
203 #define OPTION(t, p) { t, offsetof(struct options, p), 1 }
204 static const struct fuse_opt option_spec[] = {
205 	OPTION("--offset=%lu", offset),
206 	OPTION("--dbglevel=%u", debug_lvl),
207 	OPTION("--help", show_help),
208 	FUSE_OPT_KEY("--device=", 1),
209 	FUSE_OPT_END
210 };
211 
usage(void)212 static void usage(void)
213 {
214 	struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
215 
216 	fputs("usage: [options] IMAGE MOUNTPOINT\n\n"
217 	      "Options:\n"
218 	      "    --offset=#             skip # bytes when reading IMAGE\n"
219 	      "    --dbglevel=#           set output message level to # (maximum 9)\n"
220 	      "    --device=#             specify an extra device to be used together\n"
221 #if FUSE_MAJOR_VERSION < 3
222 	      "    --help                 display this help and exit\n"
223 #endif
224 	      "\n", stderr);
225 
226 #if FUSE_MAJOR_VERSION >= 3
227 	fuse_cmdline_help();
228 #else
229 	fuse_opt_add_arg(&args, ""); /* progname */
230 	fuse_opt_add_arg(&args, "-ho"); /* progname */
231 	fuse_parse_cmdline(&args, NULL, NULL, NULL);
232 #endif
233 	exit(EXIT_FAILURE);
234 }
235 
erofsfuse_dumpcfg(void)236 static void erofsfuse_dumpcfg(void)
237 {
238 	erofs_dump("disk: %s\n", fusecfg.disk);
239 	erofs_dump("offset: %llu\n", fusecfg.offset | 0ULL);
240 	erofs_dump("mountpoint: %s\n", fusecfg.mountpoint);
241 	erofs_dump("dbglevel: %u\n", cfg.c_dbg_lvl);
242 }
243 
optional_opt_func(void * data,const char * arg,int key,struct fuse_args * outargs)244 static int optional_opt_func(void *data, const char *arg, int key,
245 			     struct fuse_args *outargs)
246 {
247 	int ret;
248 
249 	switch (key) {
250 	case 1:
251 		ret = blob_open_ro(&sbi, arg + sizeof("--device=") - 1);
252 		if (ret)
253 			return -1;
254 		++sbi.extra_devices;
255 		return 0;
256 	case FUSE_OPT_KEY_NONOPT:
257 		if (fusecfg.mountpoint)
258 			return -1; /* Too many args */
259 
260 		if (!fusecfg.disk) {
261 			fusecfg.disk = strdup(arg);
262 			return 0;
263 		}
264 		if (!fusecfg.mountpoint)
265 			fusecfg.mountpoint = strdup(arg);
266 	case FUSE_OPT_KEY_OPT:
267 		if (!strcmp(arg, "-d"))
268 			fusecfg.odebug = true;
269 		break;
270 	default:
271 		DBG_BUGON(1);
272 		break;
273 	}
274 	return 1;
275 }
276 
277 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
278 #include <execinfo.h>
279 
signal_handle_sigsegv(int signal)280 static void signal_handle_sigsegv(int signal)
281 {
282 	void *array[10];
283 	size_t nptrs;
284 	char **strings;
285 	size_t i;
286 
287 	erofs_dump("========================================\n");
288 	erofs_dump("Segmentation Fault.  Starting backtrace:\n");
289 	nptrs = backtrace(array, 10);
290 	strings = backtrace_symbols(array, nptrs);
291 	if (strings) {
292 		for (i = 0; i < nptrs; i++)
293 			erofs_dump("%s\n", strings[i]);
294 		free(strings);
295 	}
296 	erofs_dump("========================================\n");
297 	abort();
298 }
299 #endif
300 
main(int argc,char * argv[])301 int main(int argc, char *argv[])
302 {
303 	int ret;
304 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
305 
306 	erofs_init_configure();
307 	printf("%s %s\n", basename(argv[0]), cfg.c_version);
308 
309 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
310 	if (signal(SIGSEGV, signal_handle_sigsegv) == SIG_ERR) {
311 		fprintf(stderr, "failed to initialize signals\n");
312 		ret = -errno;
313 		goto err;
314 	}
315 #endif
316 
317 	/* parse options */
318 	ret = fuse_opt_parse(&args, &fusecfg, option_spec, optional_opt_func);
319 	if (ret)
320 		goto err;
321 
322 	if (fusecfg.show_help || !fusecfg.mountpoint)
323 		usage();
324 	cfg.c_dbg_lvl = fusecfg.debug_lvl;
325 
326 	if (fusecfg.odebug && cfg.c_dbg_lvl < EROFS_DBG)
327 		cfg.c_dbg_lvl = EROFS_DBG;
328 
329 	cfg.c_offset = fusecfg.offset;
330 
331 	erofsfuse_dumpcfg();
332 	ret = dev_open_ro(&sbi, fusecfg.disk);
333 	if (ret) {
334 		fprintf(stderr, "failed to open: %s\n", fusecfg.disk);
335 		goto err_fuse_free_args;
336 	}
337 
338 	ret = erofs_read_superblock(&sbi);
339 	if (ret) {
340 		fprintf(stderr, "failed to read erofs super block\n");
341 		goto err_dev_close;
342 	}
343 
344 	ret = fuse_main(args.argc, args.argv, &erofs_ops, NULL);
345 
346 	erofs_put_super(&sbi);
347 err_dev_close:
348 	blob_closeall(&sbi);
349 	dev_close(&sbi);
350 err_fuse_free_args:
351 	fuse_opt_free_args(&args);
352 err:
353 	erofs_exit_configure();
354 	return ret ? EXIT_FAILURE : EXIT_SUCCESS;
355 }
356