/** * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g. * * Copyright (c) 2010-2011 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program/include file is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (in the main directory of the NTFS-3G * distribution in the file COPYING); if not, write to the Free Software * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include #include #include "inode.h" #include "security.h" #include "xattrs.h" #include "ntfs-3g_common.h" #include "misc.h" const char xattr_ntfs_3g[] = "ntfs-3g."; const char nf_ns_user_prefix[] = "user."; const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; const char nf_ns_system_prefix[] = "system."; const int nf_ns_system_prefix_len = sizeof(nf_ns_system_prefix) - 1; const char nf_ns_security_prefix[] = "security."; const int nf_ns_security_prefix_len = sizeof(nf_ns_security_prefix) - 1; const char nf_ns_trusted_prefix[] = "trusted."; const int nf_ns_trusted_prefix_len = sizeof(nf_ns_trusted_prefix) - 1; static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo"; #ifdef HAVE_SETXATTR static const char def_opts[] = "allow_other,nonempty,"; /* * Table of recognized options * Their order may be significant * The options invalid in some configuration should still * be present, so that an error can be returned */ const struct DEFOPTION optionlist[] = { { "ro", OPT_RO, FLGOPT_APPEND | FLGOPT_BOGUS }, { "noatime", OPT_NOATIME, FLGOPT_BOGUS }, { "atime", OPT_ATIME, FLGOPT_BOGUS }, { "relatime", OPT_RELATIME, FLGOPT_BOGUS }, { "fake_rw", OPT_FAKE_RW, FLGOPT_BOGUS }, { "fsname", OPT_FSNAME, FLGOPT_NOSUPPORT }, { "no_def_opts", OPT_NO_DEF_OPTS, FLGOPT_BOGUS }, { "default_permissions", OPT_DEFAULT_PERMISSIONS, FLGOPT_BOGUS }, { "permissions", OPT_PERMISSIONS, FLGOPT_BOGUS }, { "acl", OPT_ACL, FLGOPT_BOGUS }, { "umask", OPT_UMASK, FLGOPT_OCTAL }, { "fmask", OPT_FMASK, FLGOPT_OCTAL }, { "dmask", OPT_DMASK, FLGOPT_OCTAL }, { "uid", OPT_UID, FLGOPT_DECIMAL }, { "gid", OPT_GID, FLGOPT_DECIMAL }, { "show_sys_files", OPT_SHOW_SYS_FILES, FLGOPT_BOGUS }, { "hide_hid_files", OPT_HIDE_HID_FILES, FLGOPT_BOGUS }, { "hide_dot_files", OPT_HIDE_DOT_FILES, FLGOPT_BOGUS }, { "ignore_case", OPT_IGNORE_CASE, FLGOPT_BOGUS }, { "windows_names", OPT_WINDOWS_NAMES, FLGOPT_BOGUS }, { "compression", OPT_COMPRESSION, FLGOPT_BOGUS }, { "nocompression", OPT_NOCOMPRESSION, FLGOPT_BOGUS }, { "silent", OPT_SILENT, FLGOPT_BOGUS }, { "recover", OPT_RECOVER, FLGOPT_BOGUS }, { "norecover", OPT_NORECOVER, FLGOPT_BOGUS }, { "remove_hiberfile", OPT_REMOVE_HIBERFILE, FLGOPT_BOGUS }, { "sync", OPT_SYNC, FLGOPT_BOGUS | FLGOPT_APPEND }, { "locale", OPT_LOCALE, FLGOPT_STRING }, { "nfconv", OPT_NFCONV, FLGOPT_BOGUS }, { "nonfconv", OPT_NONFCONV, FLGOPT_BOGUS }, { "streams_interface", OPT_STREAMS_INTERFACE, FLGOPT_STRING }, { "user_xattr", OPT_USER_XATTR, FLGOPT_BOGUS }, { "noauto", OPT_NOAUTO, FLGOPT_BOGUS }, { "debug", OPT_DEBUG, FLGOPT_BOGUS }, { "no_detach", OPT_NO_DETACH, FLGOPT_BOGUS }, { "remount", OPT_REMOUNT, FLGOPT_BOGUS }, { "blksize", OPT_BLKSIZE, FLGOPT_STRING }, { "inherit", OPT_INHERIT, FLGOPT_BOGUS }, { "addsecurids", OPT_ADDSECURIDS, FLGOPT_BOGUS }, { "staticgrps", OPT_STATICGRPS, FLGOPT_BOGUS }, { "usermapping", OPT_USERMAPPING, FLGOPT_STRING }, { "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING }, { "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS }, { (const char*)NULL, 0, 0 } /* end marker */ } ; #define STRAPPEND_MAX_INSIZE 8192 #define strappend_is_large(x) ((x) > STRAPPEND_MAX_INSIZE) int ntfs_strappend(char **dest, const char *append) { char *p; size_t size_append, size_dest = 0; if (!dest) return -1; if (!append) return 0; size_append = strlen(append); if (*dest) size_dest = strlen(*dest); if (strappend_is_large(size_dest) || strappend_is_large(size_append)) { errno = EOVERFLOW; ntfs_log_perror("%s: Too large input buffer", EXEC_NAME); return -1; } p = (char*)realloc(*dest, size_dest + size_append + 1); if (!p) { ntfs_log_perror("%s: Memory realloction failed", EXEC_NAME); return -1; } *dest = p; strcpy(*dest + size_dest, append); return 0; } static int bogus_option_value(char *val, const char *s) { if (val) { ntfs_log_error("'%s' option shouldn't have value.\n", s); return -1; } return 0; } static int missing_option_value(char *val, const char *s) { if (!val) { ntfs_log_error("'%s' option should have a value.\n", s); return -1; } return 0; } char *parse_mount_options(ntfs_fuse_context_t *ctx, const struct ntfs_options *popts, BOOL low_fuse) { char *options, *s, *opt, *val, *ret = NULL; const char *orig_opts = popts->options; BOOL no_def_opts = FALSE; int default_permissions = 0; int permissions = 0; int acl = 0; int want_permissions = 0; int intarg; const struct DEFOPTION *poptl; ctx->secure_flags = 0; #ifdef HAVE_SETXATTR /* extended attributes interface required */ ctx->efs_raw = FALSE; #endif /* HAVE_SETXATTR */ ctx->compression = DEFAULT_COMPRESSION; ctx->atime = ATIME_ENABLED; options = strdup(orig_opts ? orig_opts : ""); if (!options) { ntfs_log_perror("%s: strdup failed", EXEC_NAME); return NULL; } s = options; while (s && *s && (val = strsep(&s, ","))) { opt = strsep(&val, "="); poptl = optionlist; while (poptl->name && strcmp(poptl->name,opt)) poptl++; if (poptl->name) { if ((poptl->flags & FLGOPT_BOGUS) && bogus_option_value(val, opt)) goto err_exit; if ((poptl->flags & FLGOPT_OCTAL) && (!val || !sscanf(val, "%o", &intarg))) { ntfs_log_error("'%s' option needs an octal value\n", opt); goto err_exit; } if ((poptl->flags & FLGOPT_DECIMAL) && (!val || !sscanf(val, "%i", &intarg))) { ntfs_log_error("'%s' option needs a decimal value\n", opt); goto err_exit; } if ((poptl->flags & FLGOPT_STRING) && missing_option_value(val, opt)) goto err_exit; switch (poptl->type) { case OPT_RO : case OPT_FAKE_RW : ctx->ro = TRUE; break; case OPT_NOATIME : ctx->atime = ATIME_DISABLED; break; case OPT_ATIME : ctx->atime = ATIME_ENABLED; break; case OPT_RELATIME : ctx->atime = ATIME_RELATIVE; break; case OPT_NO_DEF_OPTS : no_def_opts = TRUE; /* Don't add default options. */ ctx->silent = FALSE; /* cancel default silent */ break; case OPT_DEFAULT_PERMISSIONS : default_permissions = 1; break; case OPT_PERMISSIONS : permissions = 1; break; #if POSIXACLS case OPT_ACL : acl = 1; break; #endif case OPT_UMASK : ctx->dmask = ctx->fmask = intarg; want_permissions = 1; break; case OPT_FMASK : ctx->fmask = intarg; want_permissions = 1; break; case OPT_DMASK : ctx->dmask = intarg; want_permissions = 1; break; case OPT_UID : ctx->uid = intarg; want_permissions = 1; break; case OPT_GID : ctx->gid = intarg; want_permissions = 1; break; case OPT_SHOW_SYS_FILES : ctx->show_sys_files = TRUE; break; case OPT_HIDE_HID_FILES : ctx->hide_hid_files = TRUE; break; case OPT_HIDE_DOT_FILES : ctx->hide_dot_files = TRUE; break; case OPT_WINDOWS_NAMES : ctx->windows_names = TRUE; break; case OPT_IGNORE_CASE : if (low_fuse) ctx->ignore_case = TRUE; else { ntfs_log_error("'%s' is an unsupported option.\n", poptl->name); goto err_exit; } break; case OPT_COMPRESSION : ctx->compression = TRUE; break; case OPT_NOCOMPRESSION : ctx->compression = FALSE; break; case OPT_SILENT : ctx->silent = TRUE; break; case OPT_RECOVER : ctx->recover = TRUE; break; case OPT_NORECOVER : ctx->recover = FALSE; break; case OPT_REMOVE_HIBERFILE : ctx->hiberfile = TRUE; break; case OPT_SYNC : ctx->sync = TRUE; break; case OPT_LOCALE : ntfs_set_char_encoding(val); break; #if defined(__APPLE__) || defined(__DARWIN__) #ifdef ENABLE_NFCONV case OPT_NFCONV : if (ntfs_macosx_normalize_filenames(1)) { ntfs_log_error("ntfs_macosx_normalize_filenames(1) failed!\n"); goto err_exit; } break; case OPT_NONFCONV : if (ntfs_macosx_normalize_filenames(0)) { ntfs_log_error("ntfs_macosx_normalize_filenames(0) failed!\n"); goto err_exit; } break; #endif /* ENABLE_NFCONV */ #endif /* defined(__APPLE__) || defined(__DARWIN__) */ case OPT_STREAMS_INTERFACE : if (!strcmp(val, "none")) ctx->streams = NF_STREAMS_INTERFACE_NONE; else if (!strcmp(val, "xattr")) ctx->streams = NF_STREAMS_INTERFACE_XATTR; else if (!strcmp(val, "openxattr")) ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR; else if (!low_fuse && !strcmp(val, "windows")) ctx->streams = NF_STREAMS_INTERFACE_WINDOWS; else { ntfs_log_error("Invalid named data streams " "access interface.\n"); goto err_exit; } break; case OPT_USER_XATTR : ctx->streams = NF_STREAMS_INTERFACE_XATTR; break; case OPT_NOAUTO : /* Don't pass noauto option to fuse. */ break; case OPT_DEBUG : ctx->debug = TRUE; ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); break; case OPT_NO_DETACH : ctx->no_detach = TRUE; break; case OPT_REMOUNT : ntfs_log_error("Remounting is not supported at present." " You have to umount volume and then " "mount it once again.\n"); goto err_exit; case OPT_BLKSIZE : ntfs_log_info("WARNING: blksize option is ignored " "because ntfs-3g must calculate it.\n"); break; case OPT_INHERIT : /* * do not overwrite inherited permissions * in create() */ ctx->inherit = TRUE; break; case OPT_ADDSECURIDS : /* * create security ids for files being read * with an individual security attribute */ ctx->secure_flags |= (1 << SECURITY_ADDSECURIDS); break; case OPT_STATICGRPS : /* * use static definition of groups * for file access control */ ctx->secure_flags |= (1 << SECURITY_STATICGRPS); break; case OPT_USERMAPPING : ctx->usermap_path = strdup(val); if (!ctx->usermap_path) { ntfs_log_error("no more memory to store " "'usermapping' option.\n"); goto err_exit; } break; #ifdef HAVE_SETXATTR /* extended attributes interface required */ #ifdef XATTR_MAPPINGS case OPT_XATTRMAPPING : ctx->xattrmap_path = strdup(val); if (!ctx->xattrmap_path) { ntfs_log_error("no more memory to store " "'xattrmapping' option.\n"); goto err_exit; } break; #endif /* XATTR_MAPPINGS */ case OPT_EFS_RAW : ctx->efs_raw = TRUE; break; #endif /* HAVE_SETXATTR */ case OPT_FSNAME : /* Filesystem name. */ /* * We need this to be able to check whether filesystem * mounted or not. * (falling through to default) */ default : ntfs_log_error("'%s' is an unsupported option.\n", poptl->name); goto err_exit; } if ((poptl->flags & FLGOPT_APPEND) && (ntfs_strappend(&ret, poptl->name) || ntfs_strappend(&ret, ","))) goto err_exit; } else { /* Probably FUSE option. */ if (ntfs_strappend(&ret, opt)) goto err_exit; if (val) { if (ntfs_strappend(&ret, "=")) goto err_exit; if (ntfs_strappend(&ret, val)) goto err_exit; } if (ntfs_strappend(&ret, ",")) goto err_exit; } } if (!no_def_opts && ntfs_strappend(&ret, def_opts)) goto err_exit; if ((default_permissions || (permissions && !acl)) && ntfs_strappend(&ret, "default_permissions,")) goto err_exit; /* The atime options exclude each other */ if (ctx->atime == ATIME_RELATIVE && ntfs_strappend(&ret, "relatime,")) goto err_exit; else if (ctx->atime == ATIME_ENABLED && ntfs_strappend(&ret, "atime,")) goto err_exit; else if (ctx->atime == ATIME_DISABLED && ntfs_strappend(&ret, "noatime,")) goto err_exit; if (ntfs_strappend(&ret, "fsname=")) goto err_exit; if (ntfs_strappend(&ret, popts->device)) goto err_exit; if (permissions && !acl) ctx->secure_flags |= (1 << SECURITY_DEFAULT); if (acl) ctx->secure_flags |= (1 << SECURITY_ACL); if (want_permissions) ctx->secure_flags |= (1 << SECURITY_WANTED); if (ctx->ro) ctx->secure_flags &= ~(1 << SECURITY_ADDSECURIDS); exit: free(options); return ret; err_exit: free(ret); ret = NULL; goto exit; } /** * parse_options - Read and validate the programs command line * Read the command line, verify the syntax and parse the options. * * Return: 0 success, -1 error. */ int ntfs_parse_options(struct ntfs_options *popts, void (*usage)(void), int argc, char *argv[]) { int c; static const char *sopt = "-o:hnvV"; static const struct option lopt[] = { { "options", required_argument, NULL, 'o' }, { "help", no_argument, NULL, 'h' }, { "no-mtab", no_argument, NULL, 'n' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; opterr = 0; /* We'll handle the errors, thank you. */ while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { switch (c) { case 1: /* A non-option argument */ if (!popts->device) { popts->device = ntfs_malloc(PATH_MAX + 1); if (!popts->device) return -1; /* Canonicalize device name (mtab, etc) */ if (!realpath(optarg, popts->device)) { ntfs_log_perror("%s: Failed to access " "volume '%s'", EXEC_NAME, optarg); free(popts->device); popts->device = NULL; return -1; } } else if (!popts->mnt_point) { popts->mnt_point = optarg; } else { ntfs_log_error("%s: You must specify exactly one " "device and exactly one mount " "point.\n", EXEC_NAME); return -1; } break; case 'o': if (popts->options) if (ntfs_strappend(&popts->options, ",")) return -1; if (ntfs_strappend(&popts->options, optarg)) return -1; break; case 'h': usage(); exit(9); case 'n': /* * no effect - automount passes it, meaning 'no-mtab' */ break; case 'v': /* * We must handle the 'verbose' option even if * we don't use it because mount(8) passes it. */ break; case 'V': ntfs_log_info("%s %s %s %d\n", EXEC_NAME, VERSION, FUSE_TYPE, fuse_version()); exit(0); default: ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME, argv[optind - 1]); return -1; } } if (!popts->device) { ntfs_log_error("%s: No device is specified.\n", EXEC_NAME); return -1; } if (!popts->mnt_point) { ntfs_log_error("%s: No mountpoint is specified.\n", EXEC_NAME); return -1; } return 0; } int ntfs_fuse_listxattr_common(ntfs_inode *ni, ntfs_attr_search_ctx *actx, char *list, size_t size, BOOL prefixing) { int ret = 0; char *to = list; #ifdef XATTR_MAPPINGS BOOL accepted; const struct XATTRMAPPING *item; #endif /* XATTR_MAPPINGS */ /* first list the regular user attributes (ADS) */ while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, actx)) { char *tmp_name = NULL; int tmp_name_len; if (!actx->attr->name_length) continue; tmp_name_len = ntfs_ucstombs( (ntfschar *)((u8*)actx->attr + le16_to_cpu(actx->attr->name_offset)), actx->attr->name_length, &tmp_name, 0); if (tmp_name_len < 0) { ret = -errno; goto exit; } /* * When using name spaces, do not return * security, trusted or system attributes * (filtered elsewhere anyway) * otherwise insert "user." prefix */ if (prefixing) { if ((strlen(tmp_name) > sizeof(xattr_ntfs_3g)) && !strncmp(tmp_name,xattr_ntfs_3g, sizeof(xattr_ntfs_3g)-1)) tmp_name_len = 0; else ret += tmp_name_len + nf_ns_user_prefix_len + 1; } else ret += tmp_name_len + 1; if (size && tmp_name_len) { if ((size_t)ret <= size) { if (prefixing) { strcpy(to, nf_ns_user_prefix); to += nf_ns_user_prefix_len; } strncpy(to, tmp_name, tmp_name_len); to += tmp_name_len; *to = 0; to++; } else { free(tmp_name); ret = -ERANGE; goto exit; } } free(tmp_name); } #ifdef XATTR_MAPPINGS /* now append the system attributes mapped to user space */ for (item=ni->vol->xattr_mapping; item; item=item->next) { switch (item->xattr) { case XATTR_NTFS_EFSINFO : accepted = ni->vol->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED); break; case XATTR_NTFS_REPARSE_DATA : accepted = (ni->flags & FILE_ATTR_REPARSE_POINT) != const_cpu_to_le32(0); break; // TODO : we are supposed to only return xattrs which are set // this is more complex for OBJECT_ID and DOS_NAME default : accepted = TRUE; break; } if (accepted) { ret += strlen(item->name) + 1; if (size) { if ((size_t)ret <= size) { strcpy(to, item->name); to += strlen(item->name); *to++ = 0; } else { ret = -ERANGE; goto exit; } } #else /* XATTR_MAPPINGS */ /* List efs info xattr for encrypted files */ if (ni->vol->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED)) { ret += sizeof(nf_ns_alt_xattr_efsinfo); if ((size_t)ret <= size) { memcpy(to, nf_ns_alt_xattr_efsinfo, sizeof(nf_ns_alt_xattr_efsinfo)); to += sizeof(nf_ns_alt_xattr_efsinfo); #endif /* XATTR_MAPPINGS */ } } exit : return (ret); } #endif /* HAVE_SETXATTR */