diff --git a/bash-completion/smbinfo b/bash-completion/smbinfo index d56b581..ec0d8a4 100644 --- a/bash-completion/smbinfo +++ b/bash-completion/smbinfo @@ -15,19 +15,22 @@ smb_info() filemodeinfo filepositioninfo filestandardinfo + filestreaminfo fsctl-getobjid getcompression setcompression list-snapshots quota - secdesc" + secdesc + keys + gettconinfo" case $prev in '-v'|'-h') return 0 ;; 'fileaccessinfo'|'filealigninfo'|'fileallinfo'|'filebasicinfo'|'fileeainfo'|'filefsfullsizeinfo'|\ - 'fileinternalinfo'|'filemodeinfo'|'filepositioninfo'|'filestandardinfo'|'fsctl-getobjid'|\ - 'getcompression'|'setcompression'|'list-snapshots'|'quota'|'secdesc') + 'fileinternalinfo'|'filemodeinfo'|'filepositioninfo'|'filestandardinfo'|'filestreaminfo'|'fsctl-getobjid'|\ + 'getcompression'|'setcompression'|'list-snapshots'|'quota'|'secdesc'|'keys'|'gettconinfo') local IFS=$'\n' compopt -o filenames COMPREPLY=( $(compgen -f -o dirnames -- ${cur:-""}) ) diff --git a/checkopts b/checkopts index 00c4cfd..d30ee60 100755 --- a/checkopts +++ b/checkopts @@ -23,6 +23,7 @@ import re import subprocess import argparse from pprint import pprint as P +from collections import defaultdict def extract_canonical_opts(s): """ @@ -38,52 +39,30 @@ def extract_canonical_opts(s): def extract_kernel_opts(fn): STATE_BASE = 0 - STATE_DEF = 1 - STATE_USE = 2 - STATE_EXIT = 3 + STATE_USE = 1 state = STATE_BASE - fmt2enum = {} - enum2code = {} - code = '' - current_opt = '' + name2enum = {} + enum2code = defaultdict(lambda: '') rx = RX() - def code_add(s): - if current_opt != '': - if current_opt not in enum2code: - enum2code[current_opt] = '' - enum2code[current_opt] += s - with open(fn) as f: for s in f.readlines(): - if state == STATE_EXIT: - break - - elif state == STATE_BASE: - if rx.search(r'cifs_mount_option_tokens.*\{', s): - state = STATE_DEF - elif rx.search(r'^cifs_parse_mount_options', s): + if state == STATE_BASE: + if rx.search(r'^\s*fsparam_(.*)\("([^,]+)",\s+([^,]+)(?:,\s+([^,]+))?\)', s): + fmt = rx.group(1) + name = rx.group(2) + name2enum[name] = { 'enum': rx.group(3), 'fmt': fmt } + elif rx.search(r'^\s*case (Opt_[a-zA-Z0-9_]+)', s): + current_opt = rx.group(1) state = STATE_USE - elif state == STATE_DEF: - if rx.search(r'(Opt_[a-zA-Z0-9_]+)\s*,\s*"([^"]+)"', s): - fmt = rx.group(2) - opts = extract_canonical_opts(fmt) - assert(len(opts) == 1) - name = opts[0] - fmt2enum[name] = {'enum':rx.group(1), 'fmt':fmt} - elif rx.search(r'^};', s): + elif state == STATE_USE: + enum2code[current_opt] += s + if rx.search(r'\s*break;', s): state = STATE_BASE - elif state == STATE_USE: - if rx.search(r'^\s*case (Opt_[a-zA-Z0-9_]+)', s): - current_opt = rx.group(1) - elif current_opt != '' and rx.search(r'^\s*default:', s): - state = STATE_EXIT - else: - code_add(s) - return fmt2enum, enum2code + return name2enum, enum2code def chomp(s): if s[-1] == '\n': @@ -158,23 +137,23 @@ def main(): ap.add_argument("rstfile", help="path to mount.cifs.rst") args = ap.parse_args() - fmt2enum, enum2code = extract_kernel_opts(args.cfile) + name2enum, enum2code = extract_kernel_opts(args.cfile) manopts = extract_man_opts(args.rstfile) - kernel_opts_set = set(fmt2enum.keys()) + kernel_opts_set = set(name2enum.keys()) man_opts_set = set(manopts.keys()) def opt_alias_is_doc(o): - enum = fmt2enum[o]['enum'] + enum = name2enum[o]['enum'] aliases = [] - for k,v in fmt2enum.items(): + for k,v in name2enum.items(): if k != o and v['enum'] == enum: if opt_is_doc(k): return k return None def opt_exists(o): - return o in fmt2enum + return o in name2enum def opt_is_doc(o): return o in manopts @@ -194,8 +173,8 @@ def main(): undoc_opts = kernel_opts_set - man_opts_set # group opts and their negations together for opt in sortedset(undoc_opts): - fmt = fmt2enum[opt]['fmt'] - enum = fmt2enum[opt]['enum'] + fmt = name2enum[opt]['fmt'] + enum = name2enum[opt]['enum'] code = format_code(enum2code[enum]) neg = opt_neg(opt) @@ -222,6 +201,13 @@ def main(): # group opts and their negations together for opt in sortedset(unex_opts): man = manopts[opt][0] + + # If positive opt exists and it is used, then negative opt does + # not need to exist + if opt.startswith('no') and opt[2:] in name2enum: + print(f'# skipping {opt} ({opt[2:]} exists)') + continue + print('OPTION %s ("%s") line %d' % (opt, man['fmt'], man['ln'])) diff --git a/cifs.upcall.c b/cifs.upcall.c index ff6f2bd..678b140 100644 --- a/cifs.upcall.c +++ b/cifs.upcall.c @@ -552,11 +552,6 @@ get_existing_cc(const char *env_cachename) syslog(LOG_DEBUG, "%s: default ccache is %s\n", __func__, cachename); krb5_free_string(context, cachename); } - - if (!get_tgt_time(cc)) { - krb5_cc_close(context, cc); - cc = NULL; - } return cc; } @@ -638,6 +633,49 @@ init_cc_from_keytab(const char *keytab_name, const char *user) #define CIFS_SERVICE_NAME "cifs" +static krb5_error_code check_service_ticket_exists(krb5_ccache ccache, + const char *hostname) { + + krb5_error_code rc; + krb5_creds mcreds, out_creds; + + memset(&mcreds, 0, sizeof(mcreds)); + + rc = krb5_cc_get_principal(context, ccache, &mcreds.client); + if (rc) { + syslog(LOG_DEBUG, "%s: unable to get client principal from cache: %s", + __func__, krb5_get_error_message(context, rc)); + return rc; + } + + rc = krb5_sname_to_principal(context, hostname, CIFS_SERVICE_NAME, + KRB5_NT_UNKNOWN, &mcreds.server); + if (rc) { + syslog(LOG_DEBUG, "%s: unable to convert service name (%s) to principal: %s", + __func__, hostname, krb5_get_error_message(context, rc)); + krb5_free_principal(context, mcreds.client); + return rc; + } + + rc = krb5_timeofday(context, &mcreds.times.endtime); + if (rc) { + syslog(LOG_DEBUG, "%s: unable to get time: %s", + __func__, krb5_get_error_message(context, rc)); + goto out_free_principal; + } + + rc = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES, &mcreds, &out_creds); + + if (!rc) + krb5_free_cred_contents(context, &out_creds); + +out_free_principal: + krb5_free_principal(context, mcreds.server); + krb5_free_principal(context, mcreds.client); + + return rc; +} + static int cifs_krb5_get_req(const char *host, krb5_ccache ccache, DATA_BLOB * mechtoken, DATA_BLOB * sess_key) @@ -954,6 +992,13 @@ struct decoded_args { #define MAX_USERNAME_SIZE 256 char username[MAX_USERNAME_SIZE + 1]; +#define MAX_UPCALL_STRING_LEN 6 /* "mount\0" */ + enum upcall_target_enum { + UPTARGET_UNSPECIFIED, /* not specified, defaults to app */ + UPTARGET_MOUNT, /* upcall to the mount namespace */ + UPTARGET_APP, /* upcall to the application namespace which did the mount */ + } upcall_target; + uid_t uid; uid_t creduid; pid_t pid; @@ -970,6 +1015,7 @@ struct decoded_args { #define DKD_HAVE_PID 0x20 #define DKD_HAVE_CREDUID 0x40 #define DKD_HAVE_USERNAME 0x80 +#define DKD_HAVE_UPCALL_TARGET 0x100 #define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC) int have; }; @@ -980,6 +1026,7 @@ __decode_key_description(const char *desc, struct decoded_args *arg) size_t len; char *pos; const char *tkn = desc; + arg->upcall_target = UPTARGET_UNSPECIFIED; do { pos = index(tkn, ';'); @@ -1078,6 +1125,31 @@ __decode_key_description(const char *desc, struct decoded_args *arg) } arg->have |= DKD_HAVE_VERSION; syslog(LOG_DEBUG, "ver=%d", arg->ver); + } else if (strncmp(tkn, "upcall_target=", 14) == 0) { + if (pos == NULL) + len = strlen(tkn); + else + len = pos - tkn; + + len -= 14; + if (len > MAX_UPCALL_STRING_LEN) { + syslog(LOG_ERR, "upcall_target= value too long for buffer"); + return 1; + } + if (strncmp(tkn + 14, "mount", 5) == 0) { + arg->upcall_target = UPTARGET_MOUNT; + syslog(LOG_DEBUG, "upcall_target=mount"); + } else if (strncmp(tkn + 14, "app", 3) == 0) { + arg->upcall_target = UPTARGET_APP; + syslog(LOG_DEBUG, "upcall_target=app"); + } else { + // Should never happen + syslog(LOG_ERR, "Invalid upcall_target value: %s, defaulting to app", + tkn + 14); + arg->upcall_target = UPTARGET_APP; + syslog(LOG_DEBUG, "upcall_target=app"); + } + arg->have |= DKD_HAVE_UPCALL_TARGET; } if (pos == NULL) break; @@ -1441,15 +1513,20 @@ int main(const int argc, char *const argv[]) * acceptably in containers, because we'll be looking at the correct * filesystem and have the correct network configuration. */ - rc = switch_to_process_ns(arg->pid); - if (rc == -1) { - syslog(LOG_ERR, "unable to switch to process namespace: %s", strerror(errno)); - rc = 1; - goto out; + if (arg->upcall_target == UPTARGET_APP || arg->upcall_target == UPTARGET_UNSPECIFIED) { + syslog(LOG_INFO, "upcall_target=app, switching namespaces to application thread"); + rc = switch_to_process_ns(arg->pid); + if (rc == -1) { + syslog(LOG_ERR, "unable to switch to process namespace: %s", strerror(errno)); + rc = 1; + goto out; + } + if (trim_capabilities(env_probe)) + goto out; + } else { + syslog(LOG_INFO, "upcall_target=mount, not switching namespaces to application thread"); } - if (trim_capabilities(env_probe)) - goto out; /* * The kernel doesn't pass down the gid, so we resort here to scraping @@ -1496,7 +1573,7 @@ int main(const int argc, char *const argv[]) * look at the environ file. */ env_cachename = - get_cachename_from_process_env(env_probe ? arg->pid : 0); + get_cachename_from_process_env((env_probe && (arg->upcall_target == UPTARGET_APP)) ? arg->pid : 0); rc = setuid(uid); if (rc == -1) { @@ -1516,12 +1593,26 @@ int main(const int argc, char *const argv[]) goto out; } + host = arg->hostname; ccache = get_existing_cc(env_cachename); + if (ccache != NULL) { + rc = check_service_ticket_exists(ccache, host); + if(rc == 0) { + syslog(LOG_DEBUG, "%s: valid service ticket exists in credential cache", + __func__); + } else { + if (!get_tgt_time(ccache)) { + syslog(LOG_DEBUG, "%s: valid TGT is not present in credential cache", + __func__); + krb5_cc_close(context, ccache); + ccache = NULL; + } + } + } /* Couldn't find credcache? Try to use keytab */ if (ccache == NULL && arg->username[0] != '\0') ccache = init_cc_from_keytab(keytab_name, arg->username); - host = arg->hostname; // do mech specific authorization switch (arg->sec) { diff --git a/mount.cifs.c b/mount.cifs.c index 3b7a6b3..dd7dc80 100644 --- a/mount.cifs.c +++ b/mount.cifs.c @@ -124,6 +124,7 @@ #define CRED_USER 1 #define CRED_PASS 2 #define CRED_DOM 4 +#define CRED_PASS2 5 /* * Values for parsing command line options. @@ -163,6 +164,7 @@ #define OPT_BKUPGID 31 #define OPT_NOFAIL 32 #define OPT_SNAPSHOT 33 +#define OPT_PASS2 34 #define MNT_TMP_FILE "/.mtab.cifs.XXXXXX" @@ -185,9 +187,11 @@ struct parsed_mount_info { char domain[MAX_DOMAIN_SIZE + 1]; char username[MAX_USERNAME_SIZE + 1]; char password[MOUNT_PASSWD_SIZE + 1]; + char password2[MOUNT_PASSWD_SIZE + 1]; char addrlist[MAX_ADDR_LIST_LEN]; unsigned int got_user:1; unsigned int got_password:1; + unsigned int got_password2:1; unsigned int fakemnt:1; unsigned int nomtab:1; unsigned int verboseflag:1; @@ -294,7 +298,7 @@ static int mount_usage(FILE * stream) fprintf(stream, "\n\tsign,seal,fsc,snapshot=,nosharesock,persistenthandles,"); fprintf(stream, - "\n\tresilienthandles,rdma,vers=,cruid"); + "\n\tresilienthandles,rdma,vers=,cruid,password2="); fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions"); fprintf(stream, @@ -330,21 +334,31 @@ static int mount_usage(FILE * stream) * end up getting confused for option delimiters. Copy password into pw * field, turning any commas into double commas. */ -static int set_password(struct parsed_mount_info *parsed_info, const char *src) +static int +set_password(struct parsed_mount_info *parsed_info, const char *src, + const int which_pass) { - char *dst = parsed_info->password; + int is_pass2 = which_pass == OPT_PASS2 || which_pass == CRED_PASS2; + + char *dst = is_pass2 ? + parsed_info->password2 : parsed_info->password; + unsigned int pass_length = is_pass2 ? + sizeof(parsed_info->password2) : sizeof(parsed_info->password); unsigned int i = 0, j = 0; while (src[i]) { if (src[i] == ',') dst[j++] = ','; dst[j++] = src[i++]; - if (j > sizeof(parsed_info->password)) { + if (j > pass_length) { fprintf(stderr, "Converted password too long!\n"); return EX_USAGE; } } dst[j] = '\0'; + if (is_pass2) + parsed_info->got_password2 = 1; + else parsed_info->got_password = 1; return 0; } @@ -559,6 +573,9 @@ static int parse_cred_line(char *line, char **target) /* tell the caller which value target points to */ if (strncasecmp("user", line, 4) == 0) return CRED_USER; + if (strncasecmp("pass2", line, 5) == 0 || + strncasecmp("password2", line, 9) == 0) + return CRED_PASS2; if (strncasecmp("pass", line, 4) == 0) return CRED_PASS; if (strncasecmp("dom", line, 3) == 0) @@ -623,7 +640,12 @@ static int open_cred_file(char *file_name, parsed_info->got_user = 1; break; case CRED_PASS: - i = set_password(parsed_info, temp_val); + i = set_password(parsed_info, temp_val, CRED_PASS); + if (i) + goto return_i; + break; + case CRED_PASS2: + i = set_password(parsed_info, temp_val, CRED_PASS2); if (i) goto return_i; break; @@ -652,10 +674,14 @@ static int open_cred_file(char *file_name, static int get_password_from_file(int file_descript, char *filename, - struct parsed_mount_info *parsed_info, const char *program) + struct parsed_mount_info *parsed_info, const char *program, + const int which_pass) { int rc = 0; - char buf[sizeof(parsed_info->password) + 1]; + int is_pass2 = which_pass == OPT_PASS2; + unsigned int pass_length = is_pass2 ? + sizeof(parsed_info->password2) : sizeof(parsed_info->password); + char buf[pass_length + 1]; if (filename != NULL) { rc = toggle_dac_capability(0, 1); @@ -697,7 +723,7 @@ get_password_from_file(int file_descript, char *filename, goto get_pw_exit; } - rc = set_password(parsed_info, buf); + rc = set_password(parsed_info, buf, which_pass); get_pw_exit: if (filename != NULL) @@ -727,6 +753,9 @@ static int parse_opt_token(const char *token) if (strcmp(token, "pass") == 0 || strcmp(token, "password") == 0) return OPT_PASS; + if (strcmp(token, "pass2") == 0 || + strcmp(token, "password2") == 0) + return OPT_PASS2; if (strcmp(token, "sec") == 0) return OPT_SEC; if (strcmp(token, "ip") == 0 || @@ -830,7 +859,7 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) * wide +1 for NULL, and +1 for good measure */ char txtbuf[22]; - unsigned long long snapshot; + unsigned long long snapshot = 0; struct tm tm; /* make sure we're starting from beginning */ @@ -902,18 +931,36 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) parsed_info->got_password = 1; goto nocopy; } - rc = set_password(parsed_info, value); + rc = set_password(parsed_info, value, OPT_PASS); + if (rc) + return rc; + goto nocopy; + + case OPT_PASS2: + if (parsed_info->got_password2) { + fprintf(stderr, + "password2 specified twice, ignoring second\n"); + goto nocopy; + } + if (!value || !*value) { + parsed_info->got_password2 = 1; + goto nocopy; + } + rc = set_password(parsed_info, value, OPT_PASS2); if (rc) return rc; goto nocopy; case OPT_SEC: if (value) { - if (!strncmp(value, "none", 4)) + if (!strncmp(value, "none", 4)) { parsed_info->got_password = 1; + parsed_info->got_password2 = 1; + } if (!strncmp(value, "krb5", 4)) { parsed_info->is_krb5 = 1; parsed_info->got_password = 1; + parsed_info->got_password2 = 1; } } break; @@ -1110,6 +1157,7 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) case OPT_GUEST: parsed_info->got_user = 1; parsed_info->got_password = 1; + parsed_info->got_password2 = 1; goto nocopy; case OPT_RO: *filesys_flags |= MS_RDONLY; @@ -1218,7 +1266,11 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) strlcat(out, ",", MAX_OPTIONS_LEN); out_len++; } - snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf); + rc = snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf); + if (rc < 0) { + fprintf(stderr, "Error in creating mount string\n"); + return EX_SYSERR; + } out_len = strlen(out); } if (parsed_info->is_krb5 && parsed_info->sudo_uid) { @@ -1238,7 +1290,11 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) strlcat(out, ",", MAX_OPTIONS_LEN); out_len++; } - snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf); + rc = snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf); + if (rc < 0) { + fprintf(stderr, "Error in creating mount string\n"); + return EX_SYSERR; + } out_len = strlen(out); } if (got_gid) { @@ -1254,7 +1310,11 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) strlcat(out, ",", MAX_OPTIONS_LEN); out_len++; } - snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf); + rc = snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf); + if (rc < 0) { + fprintf(stderr, "Error in creating mount string\n"); + return EX_SYSERR; + } out_len = strlen(out); } if (got_bkupuid) { @@ -1270,7 +1330,11 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) strlcat(out, ",", MAX_OPTIONS_LEN); out_len++; } - snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf); + rc = snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf); + if (rc < 0) { + fprintf(stderr, "Error in creating mount string\n"); + return EX_SYSERR; + } out_len = strlen(out); } if (got_bkupgid) { @@ -1286,7 +1350,11 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) strlcat(out, ",", MAX_OPTIONS_LEN); out_len++; } - snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf); + rc = snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf); + if (rc < 0) { + fprintf(stderr, "Error in creating mount string\n"); + return EX_SYSERR; + } out_len = strlen(out); } if (got_snapshot) { @@ -1302,7 +1370,11 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info) strlcat(out, ",", MAX_OPTIONS_LEN); out_len++; } - snprintf(out + out_len, word_len + 10, "snapshot=%s", txtbuf); + rc = snprintf(out + out_len, word_len + 10, "snapshot=%s", txtbuf); + if (rc < 0) { + fprintf(stderr, "Error in creating mount string\n"); + return EX_SYSERR; + } out_len = strlen(out); } @@ -1381,13 +1453,25 @@ static int get_pw_from_env(struct parsed_mount_info *parsed_info, const char *pr int rc = 0; if (getenv("PASSWD")) - rc = set_password(parsed_info, getenv("PASSWD")); + rc = set_password(parsed_info, getenv("PASSWD"), OPT_PASS); else if (getenv("PASSWD_FD")) rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL, - parsed_info, program); + parsed_info, program, OPT_PASS); else if (getenv("PASSWD_FILE")) rc = get_password_from_file(0, getenv("PASSWD_FILE"), - parsed_info, program); + parsed_info, program, OPT_PASS); + + if (rc < 0) + return rc; + + if (getenv("PASSWD2")) + rc = set_password(parsed_info, getenv("PASSWD2"), OPT_PASS2); + else if (getenv("PASSWD2_FD")) + rc = get_password_from_file(atoi(getenv("PASSWD2_FD")), NULL, + parsed_info, program, OPT_PASS2); + else if (getenv("PASSWD2_FILE")) + rc = get_password_from_file(0, getenv("PASSWD2_FILE"), + parsed_info, program, OPT_PASS2); return rc; } @@ -1413,6 +1497,8 @@ static struct option longopts[] = { {"domain", 1, NULL, 'd'}, {"password", 1, NULL, 'p'}, {"pass", 1, NULL, 'p'}, + {"password2", 1, NULL, 0}, + {"pass2", 1, NULL, 0}, {"credentials", 1, NULL, 'c'}, {"port", 1, NULL, 'P'}, {"sloppy", 0, NULL, 's'}, @@ -1923,7 +2009,8 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info, parsed_info->got_user = 1; } - if (!parsed_info->got_password) { + // no need to prompt for password2 + if (!parsed_info->got_password && !(parsed_info->flags & MS_REMOUNT)) { char tmp_pass[MOUNT_PASSWD_SIZE + 1]; char *prompt = NULL; @@ -1931,7 +2018,7 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info, prompt = NULL; if (get_password(prompt ? prompt : "Password: ", tmp_pass, MOUNT_PASSWD_SIZE + 1)) { - rc = set_password(parsed_info, tmp_pass); + rc = set_password(parsed_info, tmp_pass, OPT_PASS); } else { fprintf(stderr, "Error reading password, exiting\n"); rc = EX_SYSERR; @@ -2252,6 +2339,13 @@ int main(int argc, char **argv) fprintf(stderr, ",pass=********"); } + if (parsed_info->got_password2) { + strlcat(options, ",password2=", options_size); + strlcat(options, parsed_info->password2, options_size); + if (parsed_info->verboseflag) + fprintf(stderr, ",password2=********"); + } + if (parsed_info->verboseflag) fprintf(stderr, "\n"); @@ -2343,6 +2437,7 @@ int main(int argc, char **argv) mount_exit: if (parsed_info) { memset(parsed_info->password, 0, sizeof(parsed_info->password)); + memset(parsed_info->password2, 0, sizeof(parsed_info->password2)); munmap(parsed_info, sizeof(*parsed_info)); } diff --git a/mount.cifs.rst b/mount.cifs.rst index d82a13c..303e9c4 100644 --- a/mount.cifs.rst +++ b/mount.cifs.rst @@ -78,15 +78,28 @@ password=arg|pass=arg Note that a password which contains the delimiter character (i.e. a comma ',') will fail to be parsed correctly on the command line. However, the same password defined in the PASSWD environment - variable or via a credentials file (see below) or entered at the - password prompt will be read correctly. + variable or via a credentials file or entered at the password prompt + will be read correctly. + +password2=arg|pass2=arg + specifies an alternate password to help with password rotation. If + this option is not given, then the environment variable PASSWD2 is used. + If password2 is not specified directly or indirectly via an argument + to mount, mount.cifs will NOT prompt for password2. + + Note that a password2 which contains the delimiter character (i.e. a + comma ',') will fail to be parsed correctly on the command + line. However, the same password2 defined in the PASSWD2 environment + variable or via a credentials file (see below) will be read correctly. credentials=filename|cred=filename specifies a file that contains a username and/or password and - optionally the name of the workgroup. The format of the file is:: + optionally an alternate password and/or the name of the workgroup. + The format of the file is:: username=value password=value + password2=value domain=value This is preferred over having passwords in plaintext in a shared file, @@ -272,6 +285,14 @@ handlecache nohandlecache Disable caching of the share root directory handle. +max_cached_dirs=arg + The maximum number of cached directories per share. Directories are cached locally + when a lease is granted by the server, which improves performance by reducing network + traffic. + + By default, ``max_cached_dirs`` is set to 16 and can hold values between 0 + and a maximum value of 2^32 - 1. + handletimeout=arg The time (in milliseconds) for which the server should reserve the handle after a failover waiting for the client to reconnect. When mounting with @@ -381,6 +402,19 @@ seal Request encryption at the SMB layer. The encryption algorithm used is AES-128-CCM. Requires SMB3 or above (see ``vers``). +esize=arg + The minimum size (in bytes) of an encrypted read response at which the + client will offload decryption to a separate worker thread. This will work + when the number of in-flight requests are greater than 1. + + Decryption of large encrypted read responses can be computationally expensive + and, when multiple reads are in flight, offloading the work can improve + performance. + + The default is ``esize=0``, which means that all encrypted read responses are + decrypted without offload and can be set up to the maximum buffer size, which + can range from 8192 to 130048 bytes, and defaults to 16384 bytes. + rdma Connect directly to the server using SMB Direct via a RDMA adapter. Requires SMB3 or above (see ``vers``). @@ -587,7 +621,8 @@ actimeo=arg the server. Longer timeouts mean a reduced number of calls to the server but looser cache coherency. The ``actimeo`` value is a positive integer that can hold values between 0 and a maximum value of 2^30 \* - HZ (frequency of timer interrupt) setting. + HZ (frequency of timer interrupt) setting. Setting it to 0 disables + caching. acregmax=arg The maximum time (in seconds) that the CIFS client caches attributes of a @@ -601,6 +636,30 @@ acdirmax=arg If this option is not specified, then acdirmax value will be set to ``actimeo`` value, see ``actimeo`` for more details. +multichannel + This option enables multichannel feature. Multichannel is an SMB3 protocol + feature that allows client to establish multiple transport connections to an + SMB server and bind them into a single authenticated SMB session. This feature + enhances fault tolerance and increases throughput by distributing traffic + across several connections. With this mount option default is to use two + channels if the server supports multichannel. The ``max_channels`` parameter + can be specified if you desire to use more than two channels. + +max_channels=arg + This option is applicable while using ``multichannel`` feature. max_channels + option allows the user to specify the number of transport connections that + should be establised between client and server up to a limit of 16. Using + this option implicitly enables the ``multichannel`` feature. + If max_channels option not specified, ``multichannel`` feature defaults to + using 2 connections. + +closetimeo=arg + The maximum time (in seconds) that the CIFS client defers sending the final + SMB3 close when the client has a handle lease on the file. + + By default, ``closetimeo`` is set to 1 second and can hold values between 0 + and a maximum value of 2^30 \* HZ. + noposixpaths If unix extensions are enabled on a share, then the client will typically allow filenames to include any character besides '/' in a @@ -611,6 +670,11 @@ noposixpaths posixpaths Inverse of ``noposixpaths`` . +compress + **EXPERIMENTAL FEATURE** Enables over-the-wire message compression for + SMB 3.1.1 or higher mounts. Mount fails when compress is on and ``vers`` is + set to a version lower than 3.1.1. + vers=arg SMB protocol version. Allowed values are: @@ -637,6 +701,10 @@ vers=arg kernels prior to v4.13, the default was ``1.0``. For kernels between v4.13 and v4.13.5 the default is ``3.0``. +sloppy + Allows the system to ignore any unrecognized mount options that follow this + option instead of failing to mount altogether. + --verbose Print additional debugging information for the mount. Note that this parameter must be specified before the ``-o`` . For example:: @@ -897,12 +965,12 @@ The variable ``USER`` may contain the username of the person to be used to authenticate to the server. The variable can be used to set both username and password by using the format ``username%password``. -The variable ``PASSWD`` may contain the password of the person using -the client. +The variables ``PASSWD`` and ``PASSWD2`` may contain the password and the +alternate password of the person using the client, respectively. -The variable ``PASSWD_FILE`` may contain the pathname of a file to read -the password from. A single line of input is read and used as the -password. +The variables ``PASSWD_FILE`` and ``PASSWD2_FILE`` may contain the +pathname of the file to read password or password2 from, respectively. +A single line of input is read and used as the password in each case. ***** NOTES