1 /*
2  * Mounts a QEMU Copy-On-Write (QCOW) image file
3  *
4  * Copyright (C) 2010-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <file_stream.h>
24 #include <memory.h>
25 #include <system_string.h>
26 #include <types.h>
27 
28 #include <stdio.h>
29 
30 #if defined( HAVE_IO_H ) || defined( WINAPI )
31 #include <io.h>
32 #endif
33 
34 #if defined( HAVE_STDLIB_H ) || defined( WINAPI )
35 #include <stdlib.h>
36 #endif
37 
38 #if defined( HAVE_UNISTD_H )
39 #include <unistd.h>
40 #endif
41 
42 #include "mount_dokan.h"
43 #include "mount_fuse.h"
44 #include "mount_handle.h"
45 #include "qcowtools_getopt.h"
46 #include "qcowtools_i18n.h"
47 #include "qcowtools_libcerror.h"
48 #include "qcowtools_libclocale.h"
49 #include "qcowtools_libcnotify.h"
50 #include "qcowtools_libqcow.h"
51 #include "qcowtools_output.h"
52 #include "qcowtools_signal.h"
53 #include "qcowtools_unused.h"
54 
55 mount_handle_t *qcowmount_mount_handle = NULL;
56 int qcowmount_abort                    = 0;
57 
58 /* Prints usage information
59  */
usage_fprint(FILE * stream)60 void usage_fprint(
61       FILE *stream )
62 {
63 	if( stream == NULL )
64 	{
65 		return;
66 	}
67 	fprintf( stream, "Use qcowmount to mount a QEMU Copy-On-Write (QCOW) image file\n\n" );
68 
69 	fprintf( stream, "Usage: qcowmount [ -k keys ] [ -p password ] [ -X extended_options ] [ -hvV ]\n"
70 	                 "                 image mount_point\n\n" );
71 
72 	fprintf( stream, "\timage:       a QEMU Copy-On-Write (QCOW) image file\n\n" );
73 	fprintf( stream, "\tmount_point: the directory to serve as mount point\n\n" );
74 
75 	fprintf( stream, "\t-h:          shows this help\n" );
76 	fprintf( stream, "\t-k:          specify the key formatted in base16\n" );
77 	fprintf( stream, "\t-p:          specify the password/passphrase\n" );
78 	fprintf( stream, "\t-v:          verbose output to stderr, while qcowmount will remain running in the\n"
79 	                 "\t             foreground\n" );
80 	fprintf( stream, "\t-V:          print version\n" );
81 	fprintf( stream, "\t-X:          extended options to pass to sub system\n" );
82 }
83 
84 /* Signal handler for qcowmount
85  */
qcowmount_signal_handler(qcowtools_signal_t signal QCOWTOOLS_ATTRIBUTE_UNUSED)86 void qcowmount_signal_handler(
87       qcowtools_signal_t signal QCOWTOOLS_ATTRIBUTE_UNUSED )
88 {
89 	libcerror_error_t *error = NULL;
90 	static char *function    = "qcowmount_signal_handler";
91 
92 	QCOWTOOLS_UNREFERENCED_PARAMETER( signal )
93 
94 	qcowmount_abort = 1;
95 
96 	if( qcowmount_mount_handle != NULL )
97 	{
98 		if( mount_handle_signal_abort(
99 		     qcowmount_mount_handle,
100 		     &error ) != 1 )
101 		{
102 			libcnotify_printf(
103 			 "%s: unable to signal mount handle to abort.\n",
104 			 function );
105 
106 			libcnotify_print_error_backtrace(
107 			 error );
108 			libcerror_error_free(
109 			 &error );
110 		}
111 	}
112 	/* Force stdin to close otherwise any function reading it will remain blocked
113 	 */
114 #if defined( WINAPI ) && !defined( __CYGWIN__ )
115 	if( _close(
116 	     0 ) != 0 )
117 #else
118 	if( close(
119 	     0 ) != 0 )
120 #endif
121 	{
122 		libcnotify_printf(
123 		 "%s: unable to close stdin.\n",
124 		 function );
125 	}
126 }
127 
128 /* The main program
129  */
130 #if defined( HAVE_WIDE_SYSTEM_CHARACTER )
wmain(int argc,wchar_t * const argv[])131 int wmain( int argc, wchar_t * const argv[] )
132 #else
133 int main( int argc, char * const argv[] )
134 #endif
135 {
136 	libqcow_error_t *error                      = NULL;
137 	system_character_t *mount_point             = NULL;
138 	system_character_t *option_extended_options = NULL;
139 	system_character_t *option_keys             = NULL;
140 	system_character_t *option_password         = NULL;
141 	const system_character_t *path_prefix       = NULL;
142 	system_character_t *source                  = NULL;
143 	char *program                               = "qcowmount";
144 	system_integer_t option                     = 0;
145 	size_t path_prefix_size                     = 0;
146 	int result                                  = 0;
147 	int verbose                                 = 0;
148 
149 #if defined( HAVE_LIBFUSE ) || defined( HAVE_LIBOSXFUSE )
150 	struct fuse_operations qcowmount_fuse_operations;
151 
152 	struct fuse_args qcowmount_fuse_arguments   = FUSE_ARGS_INIT(0, NULL);
153 	struct fuse_chan *qcowmount_fuse_channel    = NULL;
154 	struct fuse *qcowmount_fuse_handle          = NULL;
155 
156 #elif defined( HAVE_LIBDOKAN )
157 	DOKAN_OPERATIONS qcowmount_dokan_operations;
158 	DOKAN_OPTIONS qcowmount_dokan_options;
159 #endif
160 
161 	libcnotify_stream_set(
162 	 stderr,
163 	 NULL );
164 	libcnotify_verbose_set(
165 	 1 );
166 
167 	if( libclocale_initialize(
168 	     "qcowtools",
169 	     &error ) != 1 )
170 	{
171 		fprintf(
172 		 stderr,
173 		 "Unable to initialize locale values.\n" );
174 
175 		goto on_error;
176 	}
177 	if( qcowtools_output_initialize(
178 	     _IONBF,
179 	     &error ) != 1 )
180 	{
181 		fprintf(
182 		 stderr,
183 		 "Unable to initialize output settings.\n" );
184 
185 		goto on_error;
186 	}
187 	qcowtools_output_version_fprint(
188 	 stdout,
189 	 program );
190 
191 	while( ( option = qcowtools_getopt(
192 	                   argc,
193 	                   argv,
194 	                   _SYSTEM_STRING( "hk:p:vVX:" ) ) ) != (system_integer_t) -1 )
195 	{
196 		switch( option )
197 		{
198 			case (system_integer_t) '?':
199 			default:
200 				fprintf(
201 				 stderr,
202 				 "Invalid argument: %" PRIs_SYSTEM "\n",
203 				 argv[ optind - 1 ] );
204 
205 				usage_fprint(
206 				 stdout );
207 
208 				return( EXIT_FAILURE );
209 
210 			case (system_integer_t) 'h':
211 				usage_fprint(
212 				 stdout );
213 
214 				return( EXIT_SUCCESS );
215 
216 			case (system_integer_t) 'k':
217 				option_keys = optarg;
218 
219 				break;
220 
221 			case (system_integer_t) 'p':
222 				option_password = optarg;
223 
224 				break;
225 
226 			case (system_integer_t) 'v':
227 				verbose = 1;
228 
229 				break;
230 
231 			case (system_integer_t) 'V':
232 				qcowtools_output_copyright_fprint(
233 				 stdout );
234 
235 				return( EXIT_SUCCESS );
236 
237 			case (system_integer_t) 'X':
238 				option_extended_options = optarg;
239 
240 				break;
241 		}
242 	}
243 	if( optind == argc )
244 	{
245 		fprintf(
246 		 stderr,
247 		 "Missing source image.\n" );
248 
249 		usage_fprint(
250 		 stdout );
251 
252 		return( EXIT_FAILURE );
253 	}
254 	source = argv[ optind++ ];
255 
256 	if( optind == argc )
257 	{
258 		fprintf(
259 		 stderr,
260 		 "Missing mount point.\n" );
261 
262 		usage_fprint(
263 		 stdout );
264 
265 		return( EXIT_FAILURE );
266 	}
267 	mount_point = argv[ optind ];
268 
269 	libcnotify_verbose_set(
270 	 verbose );
271 	libqcow_notify_set_stream(
272 	 stderr,
273 	 NULL );
274 	libqcow_notify_set_verbose(
275 	 verbose );
276 
277 	if( mount_handle_initialize(
278 	     &qcowmount_mount_handle,
279 	     &error ) != 1 )
280 	{
281 		fprintf(
282 		 stderr,
283 		 "Unable to initialize mount handle.\n" );
284 
285 		goto on_error;
286 	}
287 	if( option_keys != NULL )
288 	{
289 		if( mount_handle_set_keys(
290 		     qcowmount_mount_handle,
291 		     option_keys,
292 		     &error ) != 1 )
293 		{
294 			fprintf(
295 			 stderr,
296 			 "Unable to set keys.\n" );
297 
298 			goto on_error;
299 		}
300 	}
301 	if( option_password != NULL )
302 	{
303 		if( mount_handle_set_password(
304 		     qcowmount_mount_handle,
305 		     option_password,
306 		     &error ) != 1 )
307 		{
308 			fprintf(
309 			 stderr,
310 			 "Unable to set password.\n" );
311 
312 			goto on_error;
313 		}
314 	}
315 #if defined( WINAPI )
316 	path_prefix = _SYSTEM_STRING( "\\QCOW" );
317 #else
318 	path_prefix = _SYSTEM_STRING( "/qcow" );
319 #endif
320 	path_prefix_size = 1 + system_string_length(
321 	                        path_prefix );
322 
323 	if( mount_handle_set_path_prefix(
324 	     qcowmount_mount_handle,
325 	     path_prefix,
326 	     path_prefix_size,
327 	     &error ) != 1 )
328 	{
329 		fprintf(
330 		 stderr,
331 		 "Unable to set path prefix.\n" );
332 
333 		goto on_error;
334 	}
335 	if( mount_handle_open(
336 	     qcowmount_mount_handle,
337 	     source,
338 	     &error ) != 1 )
339 	{
340 		fprintf(
341 		 stderr,
342 		 "Unable to open source image\n" );
343 
344 		goto on_error;
345 	}
346 	if( mount_handle_is_locked(
347 	     qcowmount_mount_handle,
348 	     &error ) != 0 )
349 	{
350 		fprintf(
351 		 stderr,
352 		 "Unable to unlock source image\n" );
353 
354 		goto on_error;
355 	}
356 #if defined( HAVE_LIBFUSE ) || defined( HAVE_LIBOSXFUSE )
357 	if( option_extended_options != NULL )
358 	{
359 		/* This argument is required but ignored
360 		 */
361 		if( fuse_opt_add_arg(
362 		     &qcowmount_fuse_arguments,
363 		     "" ) != 0 )
364 		{
365 			fprintf(
366 			 stderr,
367 			 "Unable add fuse arguments.\n" );
368 
369 			goto on_error;
370 		}
371 		if( fuse_opt_add_arg(
372 		     &qcowmount_fuse_arguments,
373 		     "-o" ) != 0 )
374 		{
375 			fprintf(
376 			 stderr,
377 			 "Unable add fuse arguments.\n" );
378 
379 			goto on_error;
380 		}
381 		if( fuse_opt_add_arg(
382 		     &qcowmount_fuse_arguments,
383 		     option_extended_options ) != 0 )
384 		{
385 			fprintf(
386 			 stderr,
387 			 "Unable add fuse arguments.\n" );
388 
389 			goto on_error;
390 		}
391 	}
392 	if( memory_set(
393 	     &qcowmount_fuse_operations,
394 	     0,
395 	     sizeof( struct fuse_operations ) ) == NULL )
396 	{
397 		fprintf(
398 		 stderr,
399 		 "Unable to clear fuse operations.\n" );
400 
401 		goto on_error;
402 	}
403 	qcowmount_fuse_operations.open       = &mount_fuse_open;
404 	qcowmount_fuse_operations.read       = &mount_fuse_read;
405 	qcowmount_fuse_operations.release    = &mount_fuse_release;
406 	qcowmount_fuse_operations.opendir    = &mount_fuse_opendir;
407 	qcowmount_fuse_operations.readdir    = &mount_fuse_readdir;
408 	qcowmount_fuse_operations.releasedir = &mount_fuse_releasedir;
409 	qcowmount_fuse_operations.getattr    = &mount_fuse_getattr;
410 	qcowmount_fuse_operations.destroy    = &mount_fuse_destroy;
411 
412 	qcowmount_fuse_channel = fuse_mount(
413 	                          mount_point,
414 	                          &qcowmount_fuse_arguments );
415 
416 	if( qcowmount_fuse_channel == NULL )
417 	{
418 		fprintf(
419 		 stderr,
420 		 "Unable to create fuse channel.\n" );
421 
422 		goto on_error;
423 	}
424 	qcowmount_fuse_handle = fuse_new(
425 	                         qcowmount_fuse_channel,
426 	                         &qcowmount_fuse_arguments,
427 	                         &qcowmount_fuse_operations,
428 	                         sizeof( struct fuse_operations ),
429 	                         qcowmount_mount_handle );
430 
431 	if( qcowmount_fuse_handle == NULL )
432 	{
433 		fprintf(
434 		 stderr,
435 		 "Unable to create fuse handle.\n" );
436 
437 		goto on_error;
438 	}
439 	if( verbose == 0 )
440 	{
441 		if( fuse_daemonize(
442 		     0 ) != 0 )
443 		{
444 			fprintf(
445 			 stderr,
446 			 "Unable to daemonize fuse.\n" );
447 
448 			goto on_error;
449 		}
450 	}
451 	result = fuse_loop(
452 	          qcowmount_fuse_handle );
453 
454 	if( result != 0 )
455 	{
456 		fprintf(
457 		 stderr,
458 		 "Unable to run fuse loop.\n" );
459 
460 		goto on_error;
461 	}
462 	fuse_destroy(
463 	 qcowmount_fuse_handle );
464 
465 	fuse_opt_free_args(
466 	 &qcowmount_fuse_arguments );
467 
468 	return( EXIT_SUCCESS );
469 
470 #elif defined( HAVE_LIBDOKAN )
471 	if( memory_set(
472 	     &qcowmount_dokan_operations,
473 	     0,
474 	     sizeof( DOKAN_OPERATIONS ) ) == NULL )
475 	{
476 		fprintf(
477 		 stderr,
478 		 "Unable to clear dokan operations.\n" );
479 
480 		goto on_error;
481 	}
482 	if( memory_set(
483 	     &qcowmount_dokan_options,
484 	     0,
485 	     sizeof( DOKAN_OPTIONS ) ) == NULL )
486 	{
487 		fprintf(
488 		 stderr,
489 		 "Unable to clear dokan options.\n" );
490 
491 		goto on_error;
492 	}
493 	qcowmount_dokan_options.Version     = DOKAN_VERSION;
494 	qcowmount_dokan_options.ThreadCount = 0;
495 	qcowmount_dokan_options.MountPoint  = mount_point;
496 
497 	if( verbose != 0 )
498 	{
499 		qcowmount_dokan_options.Options |= DOKAN_OPTION_STDERR;
500 #if defined( HAVE_DEBUG_OUTPUT )
501 		qcowmount_dokan_options.Options |= DOKAN_OPTION_DEBUG;
502 #endif
503 	}
504 /* This will only affect the drive properties
505 	qcowmount_dokan_options.Options |= DOKAN_OPTION_REMOVABLE;
506 */
507 
508 #if ( DOKAN_VERSION >= 600 ) && ( DOKAN_VERSION < 800 )
509 	qcowmount_dokan_options.Options |= DOKAN_OPTION_KEEP_ALIVE;
510 
511 	qcowmount_dokan_operations.CreateFile           = &mount_dokan_CreateFile;
512 	qcowmount_dokan_operations.OpenDirectory        = &mount_dokan_OpenDirectory;
513 	qcowmount_dokan_operations.CreateDirectory      = NULL;
514 	qcowmount_dokan_operations.Cleanup              = NULL;
515 	qcowmount_dokan_operations.CloseFile            = &mount_dokan_CloseFile;
516 	qcowmount_dokan_operations.ReadFile             = &mount_dokan_ReadFile;
517 	qcowmount_dokan_operations.WriteFile            = NULL;
518 	qcowmount_dokan_operations.FlushFileBuffers     = NULL;
519 	qcowmount_dokan_operations.GetFileInformation   = &mount_dokan_GetFileInformation;
520 	qcowmount_dokan_operations.FindFiles            = &mount_dokan_FindFiles;
521 	qcowmount_dokan_operations.FindFilesWithPattern = NULL;
522 	qcowmount_dokan_operations.SetFileAttributes    = NULL;
523 	qcowmount_dokan_operations.SetFileTime          = NULL;
524 	qcowmount_dokan_operations.DeleteFile           = NULL;
525 	qcowmount_dokan_operations.DeleteDirectory      = NULL;
526 	qcowmount_dokan_operations.MoveFile             = NULL;
527 	qcowmount_dokan_operations.SetEndOfFile         = NULL;
528 	qcowmount_dokan_operations.SetAllocationSize    = NULL;
529 	qcowmount_dokan_operations.LockFile             = NULL;
530 	qcowmount_dokan_operations.UnlockFile           = NULL;
531 	qcowmount_dokan_operations.GetFileSecurity      = NULL;
532 	qcowmount_dokan_operations.SetFileSecurity      = NULL;
533 	qcowmount_dokan_operations.GetDiskFreeSpace     = NULL;
534 	qcowmount_dokan_operations.GetVolumeInformation = &mount_dokan_GetVolumeInformation;
535 	qcowmount_dokan_operations.Unmount              = &mount_dokan_Unmount;
536 
537 #else
538 	qcowmount_dokan_operations.ZwCreateFile         = &mount_dokan_ZwCreateFile;
539 	qcowmount_dokan_operations.Cleanup              = NULL;
540 	qcowmount_dokan_operations.CloseFile            = &mount_dokan_CloseFile;
541 	qcowmount_dokan_operations.ReadFile             = &mount_dokan_ReadFile;
542 	qcowmount_dokan_operations.WriteFile            = NULL;
543 	qcowmount_dokan_operations.FlushFileBuffers     = NULL;
544 	qcowmount_dokan_operations.GetFileInformation   = &mount_dokan_GetFileInformation;
545 	qcowmount_dokan_operations.FindFiles            = &mount_dokan_FindFiles;
546 	qcowmount_dokan_operations.FindFilesWithPattern = NULL;
547 	qcowmount_dokan_operations.SetFileAttributes    = NULL;
548 	qcowmount_dokan_operations.SetFileTime          = NULL;
549 	qcowmount_dokan_operations.DeleteFile           = NULL;
550 	qcowmount_dokan_operations.DeleteDirectory      = NULL;
551 	qcowmount_dokan_operations.MoveFile             = NULL;
552 	qcowmount_dokan_operations.SetEndOfFile         = NULL;
553 	qcowmount_dokan_operations.SetAllocationSize    = NULL;
554 	qcowmount_dokan_operations.LockFile             = NULL;
555 	qcowmount_dokan_operations.UnlockFile           = NULL;
556 	qcowmount_dokan_operations.GetFileSecurity      = NULL;
557 	qcowmount_dokan_operations.SetFileSecurity      = NULL;
558 	qcowmount_dokan_operations.GetDiskFreeSpace     = NULL;
559 	qcowmount_dokan_operations.GetVolumeInformation = &mount_dokan_GetVolumeInformation;
560 	qcowmount_dokan_operations.Unmounted            = NULL;
561 	qcowmount_dokan_operations.FindStreams          = NULL;
562 	qcowmount_dokan_operations.Mounted              = NULL;
563 
564 #endif /* ( DOKAN_VERSION >= 600 ) && ( DOKAN_VERSION < 800 ) */
565 
566 	result = DokanMain(
567 	          &qcowmount_dokan_options,
568 	          &qcowmount_dokan_operations );
569 
570 	switch( result )
571 	{
572 		case DOKAN_SUCCESS:
573 			break;
574 
575 		case DOKAN_ERROR:
576 			fprintf(
577 			 stderr,
578 			 "Unable to run dokan main: generic error\n" );
579 			break;
580 
581 		case DOKAN_DRIVE_LETTER_ERROR:
582 			fprintf(
583 			 stderr,
584 			 "Unable to run dokan main: bad drive letter\n" );
585 			break;
586 
587 		case DOKAN_DRIVER_INSTALL_ERROR:
588 			fprintf(
589 			 stderr,
590 			 "Unable to run dokan main: unable to load driver\n" );
591 			break;
592 
593 		case DOKAN_START_ERROR:
594 			fprintf(
595 			 stderr,
596 			 "Unable to run dokan main: driver error\n" );
597 			break;
598 
599 		case DOKAN_MOUNT_ERROR:
600 			fprintf(
601 			 stderr,
602 			 "Unable to run dokan main: unable to assign drive letter\n" );
603 			break;
604 
605 		case DOKAN_MOUNT_POINT_ERROR:
606 			fprintf(
607 			 stderr,
608 			 "Unable to run dokan main: mount point error\n" );
609 			break;
610 
611 		default:
612 			fprintf(
613 			 stderr,
614 			 "Unable to run dokan main: unknown error: %d\n",
615 			 result );
616 			break;
617 	}
618 	return( EXIT_SUCCESS );
619 
620 #else
621 	fprintf(
622 	 stderr,
623 	 "No sub system to mount QCOW format.\n" );
624 
625 	return( EXIT_FAILURE );
626 
627 #endif /* defined( HAVE_LIBFUSE ) || defined( HAVE_LIBOSXFUSE ) */
628 
629 on_error:
630 	if( error != NULL )
631 	{
632 		libcnotify_print_error_backtrace(
633 		 error );
634 		libcerror_error_free(
635 		 &error );
636 	}
637 #if defined( HAVE_LIBFUSE ) || defined( HAVE_LIBOSXFUSE )
638 	if( qcowmount_fuse_handle != NULL )
639 	{
640 		fuse_destroy(
641 		 qcowmount_fuse_handle );
642 	}
643 	fuse_opt_free_args(
644 	 &qcowmount_fuse_arguments );
645 #endif
646 	if( qcowmount_mount_handle != NULL )
647 	{
648 		mount_handle_free(
649 		 &qcowmount_mount_handle,
650 		 NULL );
651 	}
652 	return( EXIT_FAILURE );
653 }
654 
655