1 /* radare - LGPL - Copyright 2009-2019 // pancake */
2 
3 static const char *help_msg_m[] = {
4 	"Usage:", "m[-?*dgy] [...] ", "Mountpoints management",
5 	"m", " /mnt ext2 0", "Mount ext2 fs at /mnt with delta 0 on IO",
6 	"m", " /mnt", "Mount fs at /mnt with autodetect fs and current offset",
7 	"m", "", "List all mountpoints in human readable format",
8 	"m*", "", "Same as above, but in r2 commands",
9 	"m-/", "", "Umount given path (/)",
10 	"mL", "", "List filesystem plugins (Same as Lm)",
11 	"mc", " [file]", "Cat: Show the contents of the given file",
12 	"md", " /", "List directory contents for path",
13 	"mf", "[?] [o|n]", "Search files for given filename or for offset",
14 	"mg", " /foo [offset size]", "Get fs file/dir and dump it to disk",
15 	"mi", " /foo/bar", "Get offset and size of given file",
16 	"mj", "", "List mounted filesystems in JSON",
17 	"mo", " /foo/bar", "Open given file into a malloc://",
18 	"mp", " msdos 0", "Show partitions in msdos format at offset 0",
19 	"mp", "", "List all supported partition types",
20 	"ms", " /mnt", "Open filesystem prompt at /mnt",
21 	"mw", " [file] [data]", "Write data into file", // TODO: add mwf
22 	"my", "", "Yank contents of file into clipboard",
23 	//"TODO: support multiple mountpoints and RFile IO's (need io+core refactorn",
24 	NULL
25 };
26 
cmd_mount_init(RCore * core,RCmdDesc * parent)27 static void cmd_mount_init(RCore *core, RCmdDesc *parent) {
28 	DEFINE_CMD_DESCRIPTOR (core, m);
29 }
30 
cmd_mkdir(void * data,const char * input)31 static int cmd_mkdir(void *data, const char *input) {
32 	char *res = r_syscmd_mkdir (input);
33 	if (res) {
34 		r_cons_print (res);
35 		free (res);
36 	}
37 	return 0;
38 }
39 
cmd_mv(void * data,const char * input)40 static int cmd_mv(void *data, const char *input) {
41 	return r_syscmd_mv (input)? 1: 0;
42 }
43 
44 static char *cwd = NULL;
45 #define av_max 1024
46 
t2s(const char ch)47 static const char *t2s(const char ch) {
48 	switch (ch) {
49 	case 'f': return "file";
50 	case 'd': return "directory";
51 	case 'm': return "mountpoint";
52 	}
53 	return "unknown";
54 }
55 
cmd_mount_ls(RCore * core,const char * input)56 static void cmd_mount_ls (RCore *core, const char *input) {
57 	bool isJSON = *input == 'j';
58 	RListIter *iter;
59 	RFSFile *file;
60 	RFSRoot *root;
61 	input = r_str_trim_head_ro (input + isJSON);
62 	RList *list = r_fs_dir (core->fs, input);
63 	PJ *pj = NULL;
64 	if (isJSON) {
65 		pj = pj_new ();
66 		pj_a (pj);
67 	}
68 	if (list) {
69 		r_list_foreach (list, iter, file) {
70 			if (isJSON) {
71 				pj_o (pj);
72 				pj_ks (pj, "type", t2s(file->type));
73 				pj_ks (pj, "name", file->name);
74 				pj_end (pj);
75 			} else {
76 				r_cons_printf ("%c %s\n", file->type, file->name);
77 			}
78 		}
79 		r_list_free (list);
80 	}
81 	const char *path = *input ? input : "/";
82 	r_list_foreach (core->fs->roots, iter, root) {
83 		// TODO: adjust contents between //
84 		if (!strncmp (path, root->path, strlen (path))) {
85 			char *base = strdup (root->path);
86 			char *ls = (char *)r_str_lchr (base, '/');
87 			if (ls) {
88 				ls++;
89 				*ls = 0;
90 			}
91 			// TODO: adjust contents between //
92 			if (!strcmp (path, base)) {
93 				if (isJSON) {
94 					pj_o (pj);
95 					pj_ks (pj, "path", root->path);
96 					pj_ks (pj, "type", "mountpoint");
97 					pj_end (pj);
98 				} else {
99 					r_cons_printf ("m %s\n", root->path); //  (root->path && root->path[0]) ? root->path + 1: "");
100 				}
101 			}
102 			free (base);
103 		}
104 	}
105 	if (isJSON) {
106 		pj_end (pj);
107 		r_cons_printf ("%s\n", pj_string (pj));
108 		pj_free (pj);
109 	}
110 }
111 
cmd_mount(void * data,const char * _input)112 static int cmd_mount(void *data, const char *_input) {
113 	ut64 off = 0;
114 	char *input, *oinput, *ptr, *ptr2;
115 	RList *list;
116 	RListIter *iter;
117 	RFSFile *file;
118 	RFSRoot *root;
119 	RFSPlugin *plug;
120 	RFSPartition *part;
121 	RCore *core = (RCore *)data;
122 
123 	if (!strncmp ("kdir", _input, 4)) {
124 		return cmd_mkdir (data, _input);
125 	}
126 	if (!strncmp ("v", _input, 1)) {
127 		return cmd_mv (data, _input);
128 	}
129 	input = oinput = strdup (_input);
130 
131 	switch (*input) {
132 	case ' ':
133 		input = (char *)r_str_trim_head_ro (input + 1);
134 		ptr = strchr (input, ' ');
135 		if (ptr) {
136 			*ptr = 0;
137 			ptr = (char *)r_str_trim_head_ro (ptr + 1);
138 			ptr2 = strchr (ptr, ' ');
139 			if (ptr2) {
140 				*ptr2 = 0;
141 				off = r_num_math (core->num, ptr2+1);
142 			}
143 			input = (char *)r_str_trim_head_ro (input);
144 			ptr = (char*)r_str_trim_head_ro (ptr);
145 
146 			const char *mountp = input;
147 			const char *fstype = ptr;
148 			if (*mountp != '/') {
149 				if (*fstype != '/') {
150 					eprintf ("Invalid mountpoint\n");
151 					return 0;
152 				}
153 				mountp = ptr;
154 				fstype = input;
155 			}
156 
157 			if (!r_fs_mount (core->fs, fstype, mountp, off)) {
158 				eprintf ("Cannot mount %s\n", input);
159 			}
160 		} else {
161 			if (!(ptr = r_fs_name (core->fs, core->offset))) {
162 				eprintf ("Unknown filesystem type\n");
163 			}
164 			if (!r_fs_mount (core->fs, ptr, input, core->offset)) {
165 				eprintf ("Cannot mount %s\n", input);
166 			}
167 			free (ptr);
168 		}
169 		break;
170 	case '-':
171 		r_fs_umount (core->fs, input+1);
172 		break;
173 	case 'j':
174 		{
175 			PJ *pj = pj_new ();
176 			pj_o (pj);
177 			pj_k (pj, "mountpoints");
178 			pj_a (pj);
179 			r_list_foreach (core->fs->roots, iter, root) {
180 				pj_o (pj);
181 				pj_ks (pj, "path", root->path);
182 				pj_ks (pj, "plugin", root->p->name);
183 				pj_kn (pj, "offset", root->delta);
184 				pj_end (pj);
185 			}
186 			pj_end (pj);
187 			pj_k (pj, "plugins");
188 			pj_a (pj);
189 			r_list_foreach (core->fs->plugins, iter, plug) {
190 				pj_o (pj);
191 				pj_ks (pj, "name", plug->name);
192 				pj_ks (pj, "description", plug->desc);
193 				pj_end (pj);
194 			}
195 
196 			pj_end (pj);
197 			pj_end (pj);
198 			r_cons_printf ("%s\n", pj_string (pj));
199 			pj_free (pj);
200 		}
201 		break;
202 	case '*':
203 		r_list_foreach (core->fs->roots, iter, root) {
204 			r_cons_printf ("m %s %s 0x%"PFMT64x"\n",
205 				root-> path, root->p->name, root->delta);
206 		}
207 		break;
208 	case '\0':
209 		r_list_foreach (core->fs->roots, iter, root) {
210 			r_cons_printf ("%s\t0x%"PFMT64x"\t%s\n",
211 				root->p->name, root->delta, root->path);
212 		}
213 		break;
214 	case 'L': // "mL" list of plugins
215 		r_list_foreach (core->fs->plugins, iter, plug) {
216 			r_cons_printf ("%10s  %s\n", plug->name, plug->desc);
217 		}
218 		break;
219 	case 'l': // "ml"
220 	case 'd': // "md"
221 		cmd_mount_ls (core, input + 1);
222 		break;
223 	case 'p':
224 		input++;
225 		if (*input == ' ') {
226 			input++;
227 		}
228 		ptr = strchr (input, ' ');
229 		if (ptr) {
230 			*ptr = 0;
231 			off = r_num_math (core->num, ptr+1);
232 		}
233 		list = r_fs_partitions (core->fs, input, off);
234 		if (list) {
235 			r_list_foreach (list, iter, part) {
236 				r_cons_printf ("%d %02x 0x%010"PFMT64x" 0x%010"PFMT64x"\n",
237 					part->number, part->type,
238 					part->start, part->start+part->length);
239 			}
240 			r_list_free (list);
241 		} else {
242 			eprintf ("Cannot read partition\n");
243 		}
244 		break;
245 	case 'o': //"mo"
246 		input++;
247 		if (input[0]==' ') {
248 			input++;
249 		}
250 		file = r_fs_open (core->fs, input, false);
251 		if (file) {
252 			r_fs_read (core->fs, file, 0, file->size);
253 			char *uri = r_str_newf ("malloc://%d", file->size);
254 			RIODesc *fd = r_io_open (core->io, uri, R_PERM_RW, 0);
255 			if (fd) {
256 				r_io_desc_write (fd, file->data, file->size);
257 			}
258 		} else {
259 			eprintf ("Cannot open file\n");
260 		}
261 		break;
262 	case 'i':
263 		input++;
264 		if (input[0]==' ') {
265 			input++;
266 		}
267 		file = r_fs_open (core->fs, input, false);
268 		if (file) {
269 			// XXX: dump to file or just pipe?
270 			r_fs_read (core->fs, file, 0, file->size);
271 			r_cons_printf ("f file %d 0x%08"PFMT64x"\n", file->size, file->off);
272 			r_fs_close (core->fs, file);
273 		} else {
274 			eprintf ("Cannot open file\n");
275 		}
276 		break;
277 	case 'c': // "mc"
278 		input++;
279 		if (*input == ' ') {
280 			input++;
281 		}
282 		ptr = strchr (input, ' ');
283 		if (ptr) {
284 			*ptr++ = 0;
285 		} else {
286 			ptr = "./";
287 		}
288 		file = r_fs_open (core->fs, input, false);
289 		if (file) {
290 			r_fs_read (core->fs, file, 0, file->size);
291 			r_cons_memcat ((const char *)file->data, file->size);
292 			r_fs_close (core->fs, file);
293 			r_cons_memcat ("\n", 1);
294 		} else if (!r_fs_dir_dump (core->fs, input, ptr)) {
295 			eprintf ("Cannot open file\n");
296 		}
297 		break;
298 	case 'g': // "mg"
299 		input++;
300 		int offset = 0;
301 		int size = 0;
302 		if (*input == ' ') {
303 			input++;
304 		}
305 		ptr = strchr (input, ' ');
306 		if (ptr) {
307 			*ptr++ = 0;
308 			char *input2 = strdup (ptr++);
309 			const char *args = r_str_trim_head_ro (input2);
310 			if (args) {
311 				ptr = strchr (args, ' ');
312 				if (ptr) {
313 					*ptr++ = 0;
314 					size = r_num_math (core->num, ptr);
315 				}
316 				offset = r_num_math (core->num, args);
317 			}
318 		} else {
319 			ptr = "./";
320 		}
321 		const char *filename = r_str_trim_head_ro (input);
322 
323 		file = r_fs_open (core->fs, filename, false);
324 		if (file) {
325 			char *localFile = strdup (filename);
326 			char *slash = (char *)r_str_rchr (localFile, NULL, '/');
327 			if (slash) {
328 				memmove (localFile, slash + 1, strlen (slash));
329 			}
330 			size_t ptr = offset;
331 			int total_bytes_read = 0;
332 			int blocksize = file->size < core->blocksize ? file->size : core->blocksize;
333 			size = size > 0 ? size : file->size;
334 			if (r_file_exists (localFile) && !r_sys_truncate (localFile, 0)) {
335 				eprintf ("Cannot create file %s\n", localFile);
336 				break;
337 			}
338 			while (total_bytes_read < size && ptr < file->size) {
339 				int bytes_read = 0;
340 				if (size - total_bytes_read < blocksize) {
341 					bytes_read = r_fs_read (core->fs, file, ptr, size - total_bytes_read);
342 				} else {
343 					bytes_read = r_fs_read (core->fs, file, ptr, blocksize);
344 				}
345 				if (bytes_read > 0) {
346 					r_file_dump (localFile, file->data, bytes_read, true);
347 				}
348 				ptr += bytes_read;
349 				total_bytes_read += bytes_read;
350 			}
351 			r_fs_close (core->fs, file);
352 			if (offset) {
353 				eprintf ("File '%s' created. (offset: 0x%"PFMT64x" size: %d bytes)\n", localFile, (ut64) offset, size);
354 			} else {
355 				eprintf ("File '%s' created. (size: %d bytes)\n", localFile, size);
356 			}
357 			free (localFile);
358 		} else if (!r_fs_dir_dump (core->fs, filename, ptr)) {
359 			eprintf ("Cannot open file\n");
360 		}
361 		break;
362 	case 'f':
363 		input++;
364 		switch (*input) {
365 		case '?':
366 			r_cons_printf (
367 			"Usage: mf[no] [...]\n"
368 			" mfn /foo *.c       ; search files by name in /foo path\n"
369 			" mfo /foo 0x5e91    ; search files by offset in /foo path\n"
370 			);
371 			break;
372 		case 'n':
373 			input++;
374 			if (*input == ' ')
375 				input++;
376 			ptr = strchr (input, ' ');
377 			if (ptr) {
378 				*ptr++ = 0;
379 				list = r_fs_find_name (core->fs, input, ptr);
380 				r_list_foreach (list, iter, ptr) {
381 					r_str_trim_path (ptr);
382 					printf ("%s\n", ptr);
383 				}
384 				//XXX: r_list_purge (list);
385 			} else eprintf ("Unknown store path\n");
386 			break;
387 		case 'o':
388 			input++;
389 			if (*input == ' ')
390 				input++;
391 			ptr = strchr (input, ' ');
392 			if (ptr) {
393 				*ptr++ = 0;
394 				ut64 off = r_num_math (core->num, ptr);
395 				list = r_fs_find_off (core->fs, input, off);
396 				r_list_foreach (list, iter, ptr) {
397 					r_str_trim_path (ptr);
398 					printf ("%s\n", ptr);
399 				}
400 				//XXX: r_list_purge (list);
401 			} else eprintf ("Unknown store path\n");
402 			break;
403 		}
404 		break;
405 	case 's': // "ms"
406 		if (core->http_up) {
407 			free (oinput);
408 			return false;
409 		}
410 		input++;
411 		if (input[0] == ' ') {
412 			input++;
413 		}
414 		r_cons_set_raw (false);
415 		{
416 			RFSShell shell = {
417 				.cwd = &cwd,
418 				.set_prompt = r_line_set_prompt,
419 				.readline = r_line_readline,
420 				.hist_add = r_line_hist_add
421 			};
422 			core->rfs = &shell;
423 			core->autocomplete_type = AUTOCOMPLETE_MS;
424 			r_core_autocomplete_reload (core);
425 			r_fs_shell_prompt (&shell, core->fs, input);
426 			core->autocomplete_type = AUTOCOMPLETE_DEFAULT;
427 			r_core_autocomplete_reload (core);
428 			R_FREE (cwd);
429 		}
430 		break;
431 	case 'w':
432 		if (input[1] == ' ') {
433 			char *args = r_str_trim_dup (input + 1);
434 			char *arg = strchr (args, ' ');
435 			if (arg) {
436 				data = arg + 1;
437 			} else {
438 				data = "";
439 				// touch and truncate
440 			}
441 			RFSFile *f = r_fs_open (core->fs, args, true);
442 			if (f) {
443 				r_fs_write (core->fs, f, 0, (const ut8 *)data, strlen (data));
444 				r_fs_close (core->fs, f);
445 				r_fs_file_free (f);
446 			}
447 			free (args);
448 		} else {
449 			eprintf ("Usage: mw [file] ([data])\n");
450 		}
451 		break;
452 	case 'y':
453 		eprintf ("TODO\n");
454 		break;
455 	case '?':
456 		r_core_cmd_help (core, help_msg_m);
457 		break;
458 	}
459 	free (oinput);
460 	return 0;
461 }
462