• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g.
3  *
4  * Copyright (c) 2010-2021 Jean-Pierre Andre
5  * Copyright (c) 2010      Erik Larsson
6  *
7  * This program/include file is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as published
9  * by the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program/include file is distributed in the hope that it will be
13  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program (in the main directory of the NTFS-3G
19  * distribution in the file COPYING); if not, write to the Free Software
20  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif
30 
31 #ifdef HAVE_DLFCN_H
32 #include <dlfcn.h>
33 #endif
34 
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38 
39 #ifdef HAVE_LIMITS_H
40 #include <limits.h>
41 #endif
42 
43 #ifdef HAVE_ERRNO_H
44 #include <errno.h>
45 #endif
46 
47 #include <getopt.h>
48 #include <fuse.h>
49 
50 #include "compat.h"
51 #include "inode.h"
52 #include "dir.h"
53 #include "security.h"
54 #include "xattrs.h"
55 #include "reparse.h"
56 #include "plugin.h"
57 #include "ntfs-3g_common.h"
58 #include "realpath.h"
59 #include "misc.h"
60 
61 const char xattr_ntfs_3g[] = "ntfs-3g.";
62 
63 const char nf_ns_user_prefix[] = "user.";
64 const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1;
65 const char nf_ns_system_prefix[] = "system.";
66 const int nf_ns_system_prefix_len = sizeof(nf_ns_system_prefix) - 1;
67 const char nf_ns_security_prefix[] = "security.";
68 const int nf_ns_security_prefix_len = sizeof(nf_ns_security_prefix) - 1;
69 const char nf_ns_trusted_prefix[] = "trusted.";
70 const int nf_ns_trusted_prefix_len = sizeof(nf_ns_trusted_prefix) - 1;
71 
72 static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo";
73 
74 static const char def_opts[] = "allow_other,nonempty,";
75 
76 	/*
77 	 *	 Table of recognized options
78 	 * Their order may be significant
79 	 * The options invalid in some configuration should still
80 	 * be present, so that an error can be returned
81 	 */
82 const struct DEFOPTION optionlist[] = {
83 	{ "ro", OPT_RO, FLGOPT_APPEND | FLGOPT_BOGUS },
84 	{ "noatime", OPT_NOATIME, FLGOPT_BOGUS },
85 	{ "atime", OPT_ATIME, FLGOPT_BOGUS },
86 	{ "relatime", OPT_RELATIME, FLGOPT_BOGUS },
87 	{ "delay_mtime", OPT_DMTIME, FLGOPT_DECIMAL | FLGOPT_OPTIONAL },
88 	{ "rw", OPT_RW, FLGOPT_BOGUS },
89 	{ "fake_rw", OPT_FAKE_RW, FLGOPT_BOGUS },
90 	{ "fsname", OPT_FSNAME, FLGOPT_NOSUPPORT },
91 	{ "no_def_opts", OPT_NO_DEF_OPTS, FLGOPT_BOGUS },
92 	{ "default_permissions", OPT_DEFAULT_PERMISSIONS, FLGOPT_BOGUS },
93 	{ "permissions", OPT_PERMISSIONS, FLGOPT_BOGUS },
94 	{ "acl", OPT_ACL, FLGOPT_BOGUS },
95 	{ "umask", OPT_UMASK, FLGOPT_OCTAL },
96 	{ "fmask", OPT_FMASK, FLGOPT_OCTAL },
97 	{ "dmask", OPT_DMASK, FLGOPT_OCTAL },
98 	{ "uid", OPT_UID, FLGOPT_DECIMAL },
99 	{ "gid", OPT_GID, FLGOPT_DECIMAL },
100 	{ "show_sys_files", OPT_SHOW_SYS_FILES, FLGOPT_BOGUS },
101 	{ "hide_hid_files", OPT_HIDE_HID_FILES, FLGOPT_BOGUS },
102 	{ "hide_dot_files", OPT_HIDE_DOT_FILES, FLGOPT_BOGUS },
103 	{ "ignore_case", OPT_IGNORE_CASE, FLGOPT_BOGUS },
104 	{ "windows_names", OPT_WINDOWS_NAMES, FLGOPT_BOGUS },
105 	{ "compression", OPT_COMPRESSION, FLGOPT_BOGUS },
106 	{ "nocompression", OPT_NOCOMPRESSION, FLGOPT_BOGUS },
107 	{ "silent", OPT_SILENT, FLGOPT_BOGUS },
108 	{ "recover", OPT_RECOVER, FLGOPT_BOGUS },
109 	{ "norecover", OPT_NORECOVER, FLGOPT_BOGUS },
110 	{ "remove_hiberfile", OPT_REMOVE_HIBERFILE, FLGOPT_BOGUS },
111 	{ "sync", OPT_SYNC, FLGOPT_BOGUS | FLGOPT_APPEND },
112 	{ "big_writes", OPT_BIG_WRITES, FLGOPT_BOGUS },
113 	{ "locale", OPT_LOCALE, FLGOPT_STRING },
114 	{ "nfconv", OPT_NFCONV, FLGOPT_BOGUS },
115 	{ "nonfconv", OPT_NONFCONV, FLGOPT_BOGUS },
116 	{ "streams_interface", OPT_STREAMS_INTERFACE, FLGOPT_STRING },
117 	{ "user_xattr", OPT_USER_XATTR, FLGOPT_BOGUS },
118 	{ "noauto", OPT_NOAUTO, FLGOPT_BOGUS },
119 	{ "debug", OPT_DEBUG, FLGOPT_BOGUS },
120 	{ "no_detach", OPT_NO_DETACH, FLGOPT_BOGUS },
121 	{ "remount", OPT_REMOUNT, FLGOPT_BOGUS },
122 	{ "blksize", OPT_BLKSIZE, FLGOPT_STRING },
123 	{ "inherit", OPT_INHERIT, FLGOPT_BOGUS },
124 	{ "addsecurids", OPT_ADDSECURIDS, FLGOPT_BOGUS },
125 	{ "staticgrps", OPT_STATICGRPS, FLGOPT_BOGUS },
126 	{ "usermapping", OPT_USERMAPPING, FLGOPT_STRING },
127 	{ "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING },
128 	{ "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS },
129 	{ "posix_nlink", OPT_POSIX_NLINK, FLGOPT_BOGUS },
130 	{ "special_files", OPT_SPECIAL_FILES, FLGOPT_STRING },
131 	{ "--help", OPT_HELP, FLGOPT_BOGUS },
132 	{ "-h", OPT_HELP, FLGOPT_BOGUS },
133 	{ "--version", OPT_VERSION, FLGOPT_BOGUS },
134 	{ "-V", OPT_VERSION, FLGOPT_BOGUS },
135 	{ (const char*)NULL, 0, 0 } /* end marker */
136 } ;
137 
138 #define STRAPPEND_MAX_INSIZE   8192
139 #define strappend_is_large(x) ((x) > STRAPPEND_MAX_INSIZE)
140 
ntfs_strappend(char ** dest,const char * append)141 int ntfs_strappend(char **dest, const char *append)
142 {
143 	char *p;
144 	size_t size_append, size_dest = 0;
145 
146 	if (!dest)
147 		return -1;
148 	if (!append)
149 		return 0;
150 
151 	size_append = strlen(append);
152 	if (*dest)
153 		size_dest = strlen(*dest);
154 
155 	if (strappend_is_large(size_dest) || strappend_is_large(size_append)) {
156 		errno = EOVERFLOW;
157 		ntfs_log_perror("%s: Too large input buffer", EXEC_NAME);
158 		return -1;
159 	}
160 
161 	p = (char*)realloc(*dest, size_dest + size_append + 1);
162     	if (!p) {
163 		ntfs_log_perror("%s: Memory realloction failed", EXEC_NAME);
164 		return -1;
165 	}
166 
167 	*dest = p;
168 	strcpy(*dest + size_dest, append);
169 
170 	return 0;
171 }
172 
173 /*
174  *		Insert an option before ",fsname="
175  *	This is for keeping "fsname" as the last option, because on
176  *	Solaris device names may contain commas.
177  */
178 
ntfs_strinsert(char ** dest,const char * append)179 int ntfs_strinsert(char **dest, const char *append)
180 {
181 	char *p, *q;
182 	size_t size_append, size_dest = 0;
183 
184 	if (!dest)
185 		return -1;
186 	if (!append)
187 		return 0;
188 
189 	size_append = strlen(append);
190 	if (*dest)
191 		size_dest = strlen(*dest);
192 
193 	if (strappend_is_large(size_dest) || strappend_is_large(size_append)) {
194 		errno = EOVERFLOW;
195 		ntfs_log_perror("%s: Too large input buffer", EXEC_NAME);
196 		return -1;
197 	}
198 
199 	p = (char*)malloc(size_dest + size_append + 1);
200 	if (!p) {
201 		ntfs_log_perror("%s: Memory reallocation failed", EXEC_NAME);
202 		return -1;
203 	}
204 	strcpy(p, *dest);
205 	q = strstr(p, ",fsname=");
206 	if (q) {
207 		strcpy(q, append);
208 		q = strstr(*dest, ",fsname=");
209 		if (q)
210 			strcat(p, q);
211 		free(*dest);
212 		*dest = p;
213 	} else {
214 		free(*dest);
215 		*dest = p;
216 		strcpy(*dest + size_dest, append);
217 	}
218 	return 0;
219 }
220 
bogus_option_value(char * val,const char * s)221 static int bogus_option_value(char *val, const char *s)
222 {
223 	if (val) {
224 		ntfs_log_error("'%s' option shouldn't have value.\n", s);
225 		return -1;
226 	}
227 	return 0;
228 }
229 
missing_option_value(char * val,const char * s)230 static int missing_option_value(char *val, const char *s)
231 {
232 	if (!val) {
233 		ntfs_log_error("'%s' option should have a value.\n", s);
234 		return -1;
235 	}
236 	return 0;
237 }
238 
parse_mount_options(ntfs_fuse_context_t * ctx,const struct ntfs_options * popts,BOOL low_fuse)239 char *parse_mount_options(ntfs_fuse_context_t *ctx,
240 			const struct ntfs_options *popts, BOOL low_fuse)
241 {
242 	char *options, *s, *opt, *val, *ret = NULL;
243 	const char *orig_opts = popts->options;
244 	BOOL no_def_opts = FALSE;
245 	int default_permissions = 0;
246 	int permissions = 0;
247 	int acl = 0;
248 	int want_permissions = 0;
249 	int intarg;
250 	const struct DEFOPTION *poptl;
251 
252 	ctx->secure_flags = 0;
253 #ifdef HAVE_SETXATTR	/* extended attributes interface required */
254 	ctx->efs_raw = FALSE;
255 #endif /* HAVE_SETXATTR */
256 	ctx->compression = DEFAULT_COMPRESSION;
257 	options = strdup(orig_opts ? orig_opts : "");
258 	if (!options) {
259 		ntfs_log_perror("%s: strdup failed", EXEC_NAME);
260 		return NULL;
261 	}
262 
263 	s = options;
264 	while (s && *s && (val = strsep(&s, ","))) {
265 		opt = strsep(&val, "=");
266 		poptl = optionlist;
267 		while (poptl->name && strcmp(poptl->name,opt))
268 			poptl++;
269 		if (poptl->name) {
270 			if ((poptl->flags & FLGOPT_BOGUS)
271 			    && bogus_option_value(val, opt))
272 				goto err_exit;
273 			if ((poptl->flags & FLGOPT_OCTAL)
274 			    && (!val
275 				|| !sscanf(val, "%o", &intarg))) {
276 				ntfs_log_error("'%s' option needs an octal value\n",
277 					opt);
278 				goto err_exit;
279 			}
280 			if (poptl->flags & FLGOPT_DECIMAL) {
281 				if ((poptl->flags & FLGOPT_OPTIONAL) && !val)
282 					intarg = 0;
283 				else
284 					if (!val
285 					    || !sscanf(val, "%i", &intarg)) {
286 						ntfs_log_error("'%s' option "
287 						     "needs a decimal value\n",
288 							opt);
289 						goto err_exit;
290 					}
291 			}
292 			if ((poptl->flags & FLGOPT_STRING)
293 			    && missing_option_value(val, opt))
294 				goto err_exit;
295 
296 			switch (poptl->type) {
297 			case OPT_RO :
298 			case OPT_FAKE_RW :
299 				ctx->ro = TRUE;
300 				break;
301 			case OPT_RW :
302 				ctx->rw = TRUE;
303 				break;
304 			case OPT_NOATIME :
305 				ctx->atime = ATIME_DISABLED;
306 				break;
307 			case OPT_ATIME :
308 				ctx->atime = ATIME_ENABLED;
309 				break;
310 			case OPT_RELATIME :
311 				ctx->atime = ATIME_RELATIVE;
312 				break;
313 			case OPT_DMTIME :
314 				if (!intarg)
315 					intarg = DEFAULT_DMTIME;
316 				ctx->dmtime = intarg*10000000LL;
317 				break;
318 			case OPT_NO_DEF_OPTS :
319 				no_def_opts = TRUE; /* Don't add default options. */
320 				ctx->silent = FALSE; /* cancel default silent */
321 				break;
322 			case OPT_DEFAULT_PERMISSIONS :
323 				default_permissions = 1;
324 				break;
325 			case OPT_PERMISSIONS :
326 				permissions = 1;
327 				break;
328 #if POSIXACLS
329 			case OPT_ACL :
330 				acl = 1;
331 				break;
332 #endif
333 			case OPT_UMASK :
334 				ctx->dmask = ctx->fmask = intarg;
335 				want_permissions = 1;
336 				break;
337 			case OPT_FMASK :
338 				ctx->fmask = intarg;
339 			       	want_permissions = 1;
340 				break;
341 			case OPT_DMASK :
342 				ctx->dmask = intarg;
343 			       	want_permissions = 1;
344 				break;
345 			case OPT_UID :
346 				ctx->uid = intarg;
347 			       	want_permissions = 1;
348 				break;
349 			case OPT_GID :
350 				ctx->gid = intarg;
351 				want_permissions = 1;
352 				break;
353 			case OPT_SHOW_SYS_FILES :
354 				ctx->show_sys_files = TRUE;
355 				break;
356 			case OPT_HIDE_HID_FILES :
357 				ctx->hide_hid_files = TRUE;
358 				break;
359 			case OPT_HIDE_DOT_FILES :
360 				ctx->hide_dot_files = TRUE;
361 				break;
362 			case OPT_WINDOWS_NAMES :
363 				ctx->windows_names = TRUE;
364 				break;
365 			case OPT_IGNORE_CASE :
366 				if (low_fuse)
367 					ctx->ignore_case = TRUE;
368 				else {
369 					ntfs_log_error("'%s' is an unsupported option.\n",
370 						poptl->name);
371 					goto err_exit;
372 				}
373 				break;
374 			case OPT_COMPRESSION :
375 				ctx->compression = TRUE;
376 				break;
377 			case OPT_NOCOMPRESSION :
378 				ctx->compression = FALSE;
379 				break;
380 			case OPT_SILENT :
381 				ctx->silent = TRUE;
382 				break;
383 			case OPT_RECOVER :
384 				ctx->recover = TRUE;
385 				break;
386 			case OPT_NORECOVER :
387 				ctx->recover = FALSE;
388 				break;
389 			case OPT_REMOVE_HIBERFILE :
390 				ctx->hiberfile = TRUE;
391 				break;
392 			case OPT_SYNC :
393 				ctx->sync = TRUE;
394 				break;
395 #ifdef FUSE_CAP_BIG_WRITES
396 			case OPT_BIG_WRITES :
397 				ctx->big_writes = TRUE;
398 				break;
399 #endif
400 			case OPT_LOCALE :
401 				ntfs_set_char_encoding(val);
402 				break;
403 #if defined(__APPLE__) || defined(__DARWIN__)
404 #ifdef ENABLE_NFCONV
405 			case OPT_NFCONV :
406 				if (ntfs_macosx_normalize_filenames(1)) {
407 					ntfs_log_error("ntfs_macosx_normalize_filenames(1) failed!\n");
408 					goto err_exit;
409 				}
410 				break;
411 			case OPT_NONFCONV :
412 				if (ntfs_macosx_normalize_filenames(0)) {
413 					ntfs_log_error("ntfs_macosx_normalize_filenames(0) failed!\n");
414 					goto err_exit;
415 				}
416 				break;
417 #endif /* ENABLE_NFCONV */
418 #endif /* defined(__APPLE__) || defined(__DARWIN__) */
419 			case OPT_STREAMS_INTERFACE :
420 				if (!strcmp(val, "none"))
421 					ctx->streams = NF_STREAMS_INTERFACE_NONE;
422 				else if (!strcmp(val, "xattr"))
423 					ctx->streams = NF_STREAMS_INTERFACE_XATTR;
424 				else if (!strcmp(val, "openxattr"))
425 					ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR;
426 				else if (!low_fuse && !strcmp(val, "windows"))
427 					ctx->streams = NF_STREAMS_INTERFACE_WINDOWS;
428 				else {
429 					ntfs_log_error("Invalid named data streams "
430 						"access interface.\n");
431 					goto err_exit;
432 				}
433 				break;
434 			case OPT_USER_XATTR :
435 #if defined(__APPLE__) || defined(__DARWIN__)
436 				/* macOS builds use non-namespaced extended
437 				 * attributes by default since it matches the
438 				 * standard behaviour of macOS filesystems. */
439 				ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR;
440 #else
441 				ctx->streams = NF_STREAMS_INTERFACE_XATTR;
442 #endif
443 				break;
444 			case OPT_NOAUTO :
445 				/* Don't pass noauto option to fuse. */
446 				break;
447 			case OPT_DEBUG :
448 				ctx->debug = TRUE;
449 				ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
450 				ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
451 				break;
452 			case OPT_NO_DETACH :
453 				ctx->no_detach = TRUE;
454 				break;
455 			case OPT_REMOUNT :
456 				ntfs_log_error("Remounting is not supported at present."
457 					" You have to umount volume and then "
458 					"mount it once again.\n");
459 				goto err_exit;
460 			case OPT_BLKSIZE :
461 				ntfs_log_info("WARNING: blksize option is ignored "
462 				      "because ntfs-3g must calculate it.\n");
463 				break;
464 			case OPT_INHERIT :
465 				/*
466 				 * do not overwrite inherited permissions
467 				 * in create()
468 				 */
469 				ctx->inherit = TRUE;
470 				break;
471 			case OPT_ADDSECURIDS :
472 				/*
473 				 * create security ids for files being read
474 				 * with an individual security attribute
475 				 */
476 				ctx->secure_flags |= (1 << SECURITY_ADDSECURIDS);
477 				break;
478 			case OPT_STATICGRPS :
479 				/*
480 				 * use static definition of groups
481 				 * for file access control
482 				 */
483 				ctx->secure_flags |= (1 << SECURITY_STATICGRPS);
484 				break;
485 			case OPT_USERMAPPING :
486 				ctx->usermap_path = strdup(val);
487 				if (!ctx->usermap_path) {
488 					ntfs_log_error("no more memory to store "
489 						"'usermapping' option.\n");
490 					goto err_exit;
491 				}
492 				break;
493 #ifdef HAVE_SETXATTR	/* extended attributes interface required */
494 #ifdef XATTR_MAPPINGS
495 			case OPT_XATTRMAPPING :
496 				ctx->xattrmap_path = strdup(val);
497 				if (!ctx->xattrmap_path) {
498 					ntfs_log_error("no more memory to store "
499 						"'xattrmapping' option.\n");
500 					goto err_exit;
501 				}
502 				break;
503 #endif /* XATTR_MAPPINGS */
504 			case OPT_EFS_RAW :
505 				ctx->efs_raw = TRUE;
506 				break;
507 #endif /* HAVE_SETXATTR */
508 			case OPT_POSIX_NLINK :
509 				ctx->posix_nlink = TRUE;
510 				break;
511 			case OPT_SPECIAL_FILES :
512 				if (!strcmp(val, "interix"))
513 					ctx->special_files = NTFS_FILES_INTERIX;
514 				else if (!strcmp(val, "wsl"))
515 					ctx->special_files = NTFS_FILES_WSL;
516 				else {
517 					ntfs_log_error("Invalid special_files"
518 						" mode.\n");
519 					goto err_exit;
520 				}
521 				break;
522 			case OPT_FSNAME : /* Filesystem name. */
523 			/*
524 			 * We need this to be able to check whether filesystem
525 			 * mounted or not.
526 			 *      (falling through to default)
527 			 */
528 			case OPT_HELP : /* Could lead to unclean condition */
529 			case OPT_VERSION : /* Could lead to unclean condition */
530 			default :
531 				ntfs_log_error("'%s' is an unsupported option.\n",
532 					poptl->name);
533 				goto err_exit;
534 			}
535 			if ((poptl->flags & FLGOPT_APPEND)
536 			    && (ntfs_strappend(&ret, poptl->name)
537 				    || ntfs_strappend(&ret, ",")))
538 				goto err_exit;
539 		} else { /* Probably FUSE option. */
540 			if (ntfs_strappend(&ret, opt))
541 				goto err_exit;
542 			if (val) {
543 				if (ntfs_strappend(&ret, "="))
544 					goto err_exit;
545 				if (ntfs_strappend(&ret, val))
546 					goto err_exit;
547 			}
548 			if (ntfs_strappend(&ret, ","))
549 				goto err_exit;
550 		}
551 	}
552 	if (!no_def_opts && ntfs_strappend(&ret, def_opts))
553 		goto err_exit;
554 	if ((default_permissions || (permissions && !acl))
555 			&& ntfs_strappend(&ret, "default_permissions,"))
556 		goto err_exit;
557 			/* The atime options exclude each other */
558 	if (ctx->atime == ATIME_RELATIVE && ntfs_strappend(&ret, "relatime,"))
559 		goto err_exit;
560 	else if (ctx->atime == ATIME_ENABLED && ntfs_strappend(&ret, "atime,"))
561 		goto err_exit;
562 	else if (ctx->atime == ATIME_DISABLED && ntfs_strappend(&ret, "noatime,"))
563 		goto err_exit;
564 
565 	if (ntfs_strappend(&ret, "fsname="))
566 		goto err_exit;
567 	if (ntfs_strappend(&ret, popts->device))
568 		goto err_exit;
569 	if (permissions && !acl)
570 		ctx->secure_flags |= (1 << SECURITY_DEFAULT);
571 	if (acl)
572 		ctx->secure_flags |= (1 << SECURITY_ACL);
573 	if (want_permissions)
574 		ctx->secure_flags |= (1 << SECURITY_WANTED);
575 	if (ctx->ro) {
576 		ctx->secure_flags &= ~(1 << SECURITY_ADDSECURIDS);
577 		ctx->hiberfile = FALSE;
578 		ctx->rw = FALSE;
579 	}
580 exit:
581 	free(options);
582 	return ret;
583 err_exit:
584 	free(ret);
585 	ret = NULL;
586 	goto exit;
587 }
588 
589 /**
590  * parse_options - Read and validate the programs command line
591  * Read the command line, verify the syntax and parse the options.
592  *
593  * Return:   0 success, -1 error.
594  */
ntfs_parse_options(struct ntfs_options * popts,void (* usage)(void),int argc,char * argv[])595 int ntfs_parse_options(struct ntfs_options *popts, void (*usage)(void),
596 			int argc, char *argv[])
597 {
598 	int c;
599 
600 	static const char *sopt = "-o:hnsvV";
601 	static const struct option lopt[] = {
602 		{ "options",	 required_argument,	NULL, 'o' },
603 		{ "help",	 no_argument,		NULL, 'h' },
604 		{ "no-mtab",	 no_argument,		NULL, 'n' },
605 		{ "verbose",	 no_argument,		NULL, 'v' },
606 		{ "version",	 no_argument,		NULL, 'V' },
607 		{ NULL,		 0,			NULL,  0  }
608 	};
609 
610 	opterr = 0; /* We'll handle the errors, thank you. */
611 
612 	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
613 		switch (c) {
614 		case 1:	/* A non-option argument */
615 			if (!popts->device) {
616 				popts->device = ntfs_malloc(PATH_MAX + 1);
617 				if (!popts->device)
618 					return -1;
619 
620 				/* Canonicalize device name (mtab, etc) */
621 				popts->arg_device = optarg;
622 				if (!ntfs_realpath_canonicalize(optarg,
623 						popts->device)) {
624 					ntfs_log_perror("%s: Failed to access "
625 					     "volume '%s'", EXEC_NAME, optarg);
626 					free(popts->device);
627 					popts->device = NULL;
628 					return -1;
629 				}
630 			} else if (!popts->mnt_point) {
631 				popts->mnt_point = optarg;
632 			} else {
633 				ntfs_log_error("%s: You must specify exactly one "
634 						"device and exactly one mount "
635 						"point.\n", EXEC_NAME);
636 				return -1;
637 			}
638 			break;
639 		case 'o':
640 			if (popts->options)
641 				if (ntfs_strappend(&popts->options, ","))
642 					return -1;
643 			if (ntfs_strappend(&popts->options, optarg))
644 				return -1;
645 			break;
646 		case 'h':
647 			usage();
648 			exit(9);
649 		case 'n':
650 			/*
651 			 * no effect - automount passes it, meaning 'no-mtab'
652 			 */
653 			break;
654 		case 's':
655 			/*
656 			 * no effect - automount passes it, meaning sloppy
657 			 */
658 			break;
659 		case 'v':
660 			/*
661 			 * We must handle the 'verbose' option even if
662 			 * we don't use it because mount(8) passes it.
663 			 */
664 			break;
665 		case 'V':
666 			ntfs_log_info("%s %s %s %d\n", EXEC_NAME, VERSION,
667 				      FUSE_TYPE, fuse_version());
668 			exit(0);
669 		default:
670 			ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME,
671 				       argv[optind - 1]);
672 			return -1;
673 		}
674 	}
675 
676 	if (!popts->device) {
677 		ntfs_log_error("%s: No device is specified.\n", EXEC_NAME);
678 		return -1;
679 	}
680 	if (!popts->mnt_point) {
681 		ntfs_log_error("%s: No mountpoint is specified.\n", EXEC_NAME);
682 		return -1;
683 	}
684 
685 	return 0;
686 }
687 
688 #ifdef HAVE_SETXATTR
689 
ntfs_fuse_listxattr_common(ntfs_inode * ni,ntfs_attr_search_ctx * actx,char * list,size_t size,BOOL prefixing)690 int ntfs_fuse_listxattr_common(ntfs_inode *ni, ntfs_attr_search_ctx *actx,
691 			char *list, size_t size, BOOL prefixing)
692 {
693 	int ret = 0;
694 	char *to = list;
695 #ifdef XATTR_MAPPINGS
696 	BOOL accepted;
697 	const struct XATTRMAPPING *item;
698 #endif /* XATTR_MAPPINGS */
699 
700 		/* first list the regular user attributes (ADS) */
701 	while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE,
702 				0, NULL, 0, actx)) {
703 		char *tmp_name = NULL;
704 		int tmp_name_len;
705 
706 		if (!actx->attr->name_length)
707 			continue;
708 		tmp_name_len = ntfs_ucstombs(
709 			(ntfschar *)((u8*)actx->attr +
710 				le16_to_cpu(actx->attr->name_offset)),
711 			actx->attr->name_length, &tmp_name, 0);
712 		if (tmp_name_len < 0) {
713 			ret = -errno;
714 			goto exit;
715 		}
716 				/*
717 				 * When using name spaces, do not return
718 				 * security, trusted or system attributes
719 				 * (filtered elsewhere anyway)
720 				 * otherwise insert "user." prefix
721 				 */
722 		if (prefixing) {
723 			if ((strlen(tmp_name) > sizeof(xattr_ntfs_3g))
724 			  && !strncmp(tmp_name,xattr_ntfs_3g,
725 				sizeof(xattr_ntfs_3g)-1))
726 				tmp_name_len = 0;
727 			else
728 				ret += tmp_name_len
729 					 + nf_ns_user_prefix_len + 1;
730 		} else
731 			ret += tmp_name_len + 1;
732 		if (size && tmp_name_len) {
733 			if ((size_t)ret <= size) {
734 				if (prefixing) {
735 					strcpy(to, nf_ns_user_prefix);
736 					to += nf_ns_user_prefix_len;
737 				}
738 				strncpy(to, tmp_name, tmp_name_len);
739 				to += tmp_name_len;
740 				*to = 0;
741 				to++;
742 			} else {
743 				free(tmp_name);
744 				ret = -ERANGE;
745 				goto exit;
746 			}
747 		}
748 		free(tmp_name);
749 	}
750 #ifdef XATTR_MAPPINGS
751 		/* now append the system attributes mapped to user space */
752 	for (item=ni->vol->xattr_mapping; item; item=item->next) {
753 		switch (item->xattr) {
754 		case XATTR_NTFS_EFSINFO :
755 			accepted = ni->vol->efs_raw
756 				&& (ni->flags & FILE_ATTR_ENCRYPTED);
757 			break;
758 		case XATTR_NTFS_REPARSE_DATA :
759 			accepted = (ni->flags & FILE_ATTR_REPARSE_POINT)
760 					!= const_cpu_to_le32(0);
761 			break;
762 // TODO : we are supposed to only return xattrs which are set
763 // this is more complex for OBJECT_ID and DOS_NAME
764 		default : accepted = TRUE;
765 			break;
766 		}
767 		if (accepted) {
768 			ret += strlen(item->name) + 1;
769 			if (size) {
770 				if ((size_t)ret <= size) {
771 					strcpy(to, item->name);
772 					to += strlen(item->name);
773 					*to++ = 0;
774 				} else {
775 					ret = -ERANGE;
776 					goto exit;
777 				}
778 			}
779 #else /* XATTR_MAPPINGS */
780 		/* List efs info xattr for encrypted files */
781 	if (ni->vol->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED)) {
782 		ret += sizeof(nf_ns_alt_xattr_efsinfo);
783 		if ((size_t)ret <= size) {
784 			memcpy(to, nf_ns_alt_xattr_efsinfo,
785 				sizeof(nf_ns_alt_xattr_efsinfo));
786 			to += sizeof(nf_ns_alt_xattr_efsinfo);
787 #endif /* XATTR_MAPPINGS */
788 		}
789 	}
790 exit :
791 	return (ret);
792 }
793 
794 #endif /* HAVE_SETXATTR */
795 
796 #ifndef DISABLE_PLUGINS
797 
798 int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag,
799 				const plugin_operations_t *ops, void *handle)
800 {
801 	plugin_list_t *plugin;
802 	int res;
803 
804 	res = -1;
805 	plugin = (plugin_list_t*)ntfs_malloc(sizeof(plugin_list_t));
806 	if (plugin) {
807 		plugin->tag = tag;
808 		plugin->ops = ops;
809 		plugin->handle = handle;
810 		plugin->next = ctx->plugins;
811 		ctx->plugins = plugin;
812 		res = 0;
813 	}
814 	return (res);
815 }
816 
817 /*
818  *		Get the reparse operations associated to an inode
819  *
820  *	The plugin able to process the reparse point is dynamically loaded
821  *
822  *	When successful, returns the operations vector and the reparse
823  *		data if requested,
824  *	Otherwise returns NULL, with errno set.
825  */
826 
827 const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
828 				ntfs_inode *ni, REPARSE_POINT **reparse_wanted)
829 {
830 	const struct plugin_operations *ops;
831 	void *handle;
832 	REPARSE_POINT *reparse;
833 	le32 tag, seltag;
834 	plugin_list_t *plugin;
835 	plugin_init_t pinit;
836 
837 	ops = (struct plugin_operations*)NULL;
838 	reparse = ntfs_get_reparse_point(ni);
839 	if (reparse) {
840 		tag = reparse->reparse_tag;
841 		seltag = tag & IO_REPARSE_PLUGIN_SELECT;
842 		for (plugin=ctx->plugins; plugin && (plugin->tag != seltag);
843 						plugin = plugin->next) { }
844 		if (plugin) {
845 			ops = plugin->ops;
846 		} else {
847 #ifdef PLUGIN_DIR
848 			char name[sizeof(PLUGIN_DIR) + 64];
849 
850 			snprintf(name,sizeof(name), PLUGIN_DIR
851 					"/ntfs-plugin-%08lx.so",
852 					(long)le32_to_cpu(seltag));
853 #else
854 			char name[64];
855 
856 			snprintf(name,sizeof(name), "ntfs-plugin-%08lx.so",
857 					(long)le32_to_cpu(seltag));
858 #endif
859 			handle = dlopen(name, RTLD_LAZY);
860 			if (handle) {
861 				pinit = (plugin_init_t)dlsym(handle, "init");
862 				if (pinit) {
863 				/* pinit() should set errno if it fails */
864 					ops = (*pinit)(tag);
865 					if (ops && register_reparse_plugin(ctx,
866 							seltag, ops, handle))
867 						ops = (struct plugin_operations*)NULL;
868 				} else
869 					errno = ELIBBAD;
870 				if (!ops)
871 					dlclose(handle);
872 			} else {
873 				errno = ELIBACC;
874 				if (!(ctx->errors_logged & ERR_PLUGIN)) {
875 					ntfs_log_perror(
876 						"Could not load plugin %s",
877 						name);
878 					ntfs_log_error("Hint %s\n",dlerror());
879 				}
880 				ctx->errors_logged |= ERR_PLUGIN;
881 			}
882 		}
883 		if (ops && reparse_wanted)
884 			*reparse_wanted = reparse;
885 		else
886 			free(reparse);
887 	}
888 	return (ops);
889 }
890 
891 void close_reparse_plugins(ntfs_fuse_context_t *ctx)
892 {
893 	while (ctx->plugins) {
894 		plugin_list_t *next;
895 
896 		next = ctx->plugins->next;
897 		if (ctx->plugins->handle)
898 			dlclose(ctx->plugins->handle);
899 		free(ctx->plugins);
900 		ctx->plugins = next;
901 	}
902 }
903 
904 #endif /* DISABLE_PLUGINS */
905 
906 #ifdef HAVE_SETXATTR
907 
908 /*
909  *		Check whether a user xattr is allowed
910  *
911  *	The inode must be a plain file or a directory. The only allowed
912  *	metadata file is the root directory (useful for MacOSX and hopefully
913  *	does not harm Windows).
914  */
915 
916 BOOL user_xattrs_allowed(ntfs_fuse_context_t *ctx __attribute__((unused)),
917 			ntfs_inode *ni)
918 {
919 	u32 dt_type;
920 	BOOL res;
921 
922 		/* Quick return for common cases and root */
923 	if (!(ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))
924 	    || (ni->mft_no == FILE_root))
925 		res = TRUE;
926 	else {
927 			/* Reparse point depends on kind, see plugin */
928 		if (ni->flags & FILE_ATTR_REPARSE_POINT) {
929 #ifndef DISABLE_PLUGINS
930 			struct stat stbuf;
931 			REPARSE_POINT *reparse;
932 			const plugin_operations_t *ops;
933 
934 			res = FALSE; /* default for error cases */
935 			ops = select_reparse_plugin(ctx, ni, &reparse);
936 			if (ops) {
937 				if (ops->getattr
938 				    && !ops->getattr(ni,reparse,&stbuf)) {
939 					res = S_ISREG(stbuf.st_mode)
940 						    || S_ISDIR(stbuf.st_mode);
941 				}
942 				free(reparse);
943 			}
944 #else /* DISABLE_PLUGINS */
945 			res = FALSE; /* mountpoints, symlinks, ... */
946 #endif /* DISABLE_PLUGINS */
947 		} else {
948 				/* Metadata */
949 			if (ni->mft_no < FILE_first_user)
950 				res = FALSE;
951 			else {
952 				/* Interix types */
953 				dt_type = ntfs_interix_types(ni);
954 				res = (dt_type == NTFS_DT_REG)
955 					|| (dt_type == NTFS_DT_DIR);
956 			}
957 		}
958 	}
959 	return (res);
960 }
961 
962 #endif /* HAVE_SETXATTR */
963